Reputation: 2151
I'm just getting started on monads, and I can't figure out why these two expressions evaluate differently:
ghci> [1,2] >>= \n -> ['a','b'] >>= \ch -> return (n,ch)
[(1,'a'),(1,'b'),(2,'a'),(2,'b')]
ghci> return ([1,2],['a','b'])
([1,2],"ab")
Upvotes: 3
Views: 215
Reputation: 49105
In GHCi, you can interactively check the type of an expression using :t
. Doing so shows that your expressions have different types:
ghci> :t [1,2] >>= \n -> ['a','b'] >>= \ch -> return (n,ch)
[1,2] >>= \n -> ['a','b'] >>= \ch -> return (n,ch)
:: (Num t) => [(t, Char)]
ghci> :t return ([1,2],['a','b'])
return ([1,2],['a','b']) :: (Num t, Monad m) => m ([t], [Char])
Thus, they have different values.
Perhaps you're confused by the presence of monads inside the argument to return
. However, look at its type:
ghci> :t return
return :: Monad m => a -> m a
return
knows nothing about its argument -- it just takes a value, any value, and places it in a default, monadic context.
To understand exactly what happens when these expressions are evaluated, you'll need:
Here's the monad instance:
instance Monad [] where
m >>= k = foldr ((++) . k) [] m
m >> k = foldr ((++) . (\ _ -> k)) [] m
return x = [x]
fail _ = []
(We can ignore >>
and fail
, since we're not using them.)
So let's expand our expression:
[1,2] >>= \n -> ['a','b'] >>= \ch -> return (n,ch)
so setting m = [1, 2]
and k = \n -> ['a','b'] >>= \ch -> return (n,ch)
we get:
foldr ((++) . (\n -> ['a','b'] >>= \ch -> return (n,ch))) [] [1,2]
now to get rid of the second >>=
, m = ['a', 'b']
and k = \ch -> return (n, ch)
:
foldr ((++) . (\n -> rest)) [] [1,2]
where
rest = foldr ((++) . (\ch -> return (n,ch))) [] ['a', 'b']
and return
is easy to get rid of:
foldr ((++) . (\n -> rest)) [] [1,2]
where
rest = foldr ((++) . (\ch -> [(n,ch)]) [] ['a', 'b']
On the other hand, the value of the 2nd expression:
return ([1,2],['a','b'])
depends on which monad you're in. In the list monad, it simply becomes:
[([1,2], ['a','b'])] :: [] ([Int], String)
whereas in the Maybe monad, it's:
Just ([1,2], ['a', 'b']) :: Maybe ([Int], String)
Upvotes: 6
Reputation: 2697
The types are different, so it's reasonable that the behaviors are different
The first expression would typecheck as Num t => [(t, Char)]
The use of [] as the monad in the (>>=) means that it infers that the monad should be the List monad and in the context of the List Monad
http://en.wikibooks.org/wiki/Haskell/Understanding_monads/List (>>=) is concatMap and return is (:[]).
[1,2] >>= \n -> ['a','b'] >>= \ch -> return (n,ch)
is the same as
concatMap (\n -> concatMap (\ch -> [(n, ch)]) ['a', 'b']) [1,2]
which returns [(1,'a'),(1,'b'),(2,'a'),(2,'b')]
In your second example what's really going on is
that the type of the expression is a bit more general:
Prelude> :t return ([1,2],['a','b'])
return ([1,2],['a','b']) :: (Monad m, Num t) => m ([t], [Char])
Because you're running it in GHCi a few things happen. GHCi can be considered a very big special IO Monad. So since no monad was specified, when GHC tries to print the results, it'll take the m
Monad to be IO
in this case.
The t
is defaulted to Integer
as well, so the type of the resulting expression is :: IO ([Integer], [Char])
.
As it happens, all the types used have a Show
instance, so GHC can print the results of executing the IO
action, which in this case (due to the action being return) is the same as the input.
Upvotes: 10