Difference between revisions of "DDC/EffectSystem"
Line 13: | Line 13: | ||
</haskell> |
</haskell> |
||
− | This puts us in a situation where we really need two copies of every higher-order function, one 'pure' and one monadic - that's a lot of duplication! |
+ | This puts us in a situation where we really need two copies of every higher-order function, one 'pure' and one monadic - that's a lot of duplication! This is even more of a problem when a library (like <hask>Data.Map</hask>) exports a 'pure' version but not a monadic one. In this case we either have to refactor our code to be less monadic, or spend time writing a function that should really be in the library. |
− | With effect typing, the fact that function might cause an effect is orthogonal to |
+ | With effect typing, the fact that function might cause an effect is orthogonal to the 'shape' of its type. In Haskell, because <hask>putStr</hask> prints to the screen it has the <hask>IO</hask> constructor in its return value: |
<haskell> |
<haskell> |
||
Line 21: | Line 21: | ||
</haskell> |
</haskell> |
||
− | In Disciple this fact is encoded as an effect on the function arrow instead: |
+ | In Disciple, this fact is encoded as an effect on the function arrow instead: |
<haskell> |
<haskell> |
||
Line 28: | Line 28: | ||
</haskell> |
</haskell> |
||
− | In this type, <hask>!e1</hask> is an |
+ | In this type, <hask>!e1</hask> is an ''effect variable'', much like <hask>a</hask> is a type variable. <hask>!Console</hask> is an ''effect constructor'' and it tells tells us what else happens when this function is called, besides evaluating the return value. |
+ | |||
+ | Because this second version of <hask>putStr</hask> doesn't have the monad in its type, we can just use the plain version of <hask>map</hask> and not have to worry about <hask>mapM</hask>. |
||
+ | |||
+ | <haskell> |
||
+ | main () = map putStr ["one", "two", "three"] -- prints "onetwothree" |
||
+ | </haskell> |
Revision as of 01:15, 19 March 2008
Effect typing vs State monads
Instead of state monads (like IO
), Disciple uses default strict evaluation and effect typing to deal with computational effects. Effect typing helps us write less code because the types of effectful functions have the same shape as their 'pure' counterparts.
For example, in Haskell the 'pure' map function has type:
map :: (a -> b) -> [a] -> [b]
but if we need to pass an effectful function defined in terms of a state monad, we must use the monadic version, mapM
instead.
mapM :: Monad m => (a -> m b) -> [a] -> m [b]
This puts us in a situation where we really need two copies of every higher-order function, one 'pure' and one monadic - that's a lot of duplication! This is even more of a problem when a library (like Data.Map
) exports a 'pure' version but not a monadic one. In this case we either have to refactor our code to be less monadic, or spend time writing a function that should really be in the library.
With effect typing, the fact that function might cause an effect is orthogonal to the 'shape' of its type. In Haskell, because putStr
prints to the screen it has the IO
constructor in its return value:
putStr :: String -> IO ()
In Disciple, this fact is encoded as an effect on the function arrow instead:
putStr :: String -(!e1)> ()
:- !e1 = !Console
In this type, !e1
is an effect variable, much like a
is a type variable. !Console
is an effect constructor and it tells tells us what else happens when this function is called, besides evaluating the return value.
Because this second version of putStr
doesn't have the monad in its type, we can just use the plain version of map
and not have to worry about mapM
.
main () = map putStr ["one", "two", "three"] -- prints "onetwothree"