In Haskell, when we use a do block, how does it determine which monad to use?
Perhaps some "practical" examples will help:
foo1 = do
print 5
return 7
-- print belongs to the IO monad. This whole thing is in IO.
foo2 x = do
writeTVar x 7
return 11
-- writeTVar belongs to the STM monad. This whole thing is in STM.
foo3 = do
let x = 5
[1, 2, 3, 4]
-- Last line is a list expression. This whole thing is in the list monad.
foo4 = do
put 7
return 9
-- put is in the State monad. This whole thing is in the state monad.
foo5 = do
x <- magic1
y <- magic2
return (x, y)
-- This is in whatever monad magic1 and magic2 are in.
foo6 = do
return 13
-- Doesn't mention any monad. Works for ALL POSSIBLE MONADS!
foo7 abc def = do
x <- abc
y <- def
return (x, y)
-- Runs in whatever monad abc and def run in.
-- By passing different arguments, you can CHANGE which monad that is!
source to share
It uses the same generic type class mechanism that is used to determine which to +
use, or even which numeric type to use for literals. That is, something like
do
return True
There will be no divine concrete monad that it has to use, but just assign the type Monad m => m Bool
. This limitation says that do-block has a type m Bool
for anyone m
that turns out to have implemented a type class Monad
. Moreover, whenever this block is used, the specific instance will be taken out of the context of its use.
If we use operators bound to a specific one Monad
, this will force the type to become more specific. For example, if we use modify :: (a -> a) -> State s a -> State s ()
(I'm simplifying the type for the example here) then this will make the block have a type State s ...
. In general, Haskell will define the most general type and use class type constraints to make sure that the appropriate types implement the appropriate operations.
source to share