Why this is not throwing any type error?
I was trying some code segment from one of the blog and I came to no开发者_开发知识库tice the following code
f :: Int -> [Int]
f x = [1+x,2*x]
test :: IO ()
test = putStrLn . show $ return 5 >>= f >>= f
While executing I am getting [7,12,11,20]. Why the second 'f' function call is not throwing type error ? Is it something related to List Monad?
It is exactly because lists are monads. You have
return 5 >>= f >>= f
In the list monad, (>>=) = flip concatMap
, so that's the same as
concatMap f $ concatMap f $ return 5
Also in the list monad, return x = [x]
, so we have
concatMap f $ concatMap f [5]
Now, concatMap g x = concat (map g x)
, so we can expand that to
concat $ map f $ concat $ map f [5]
and evaluate it
concat $ map f $ concat $ [[6, 10]]
concat $ map f $ [6, 10]
concat $ [[7, 12], [11, 20]]
[7, 12, 11, 20]
Does that make sense?
Let's ask GHC what the types are for some of the subexpressions.
> :t putStrLn . show
putStrLn . show :: (Show a) => a -> IO ()
That's certainly as expected.
> :t return 5 >>= f >>= f
return 5 >>= f >>= f :: [Int]
I suppose we expected that, but it doesn't answer your question. Next, a quick reminder on how that expression parses:
> :i (>>=)
class Monad m where
(>>=) :: m a -> (a -> m b) -> m b
...
infixl 1 >>=
Okay, it's left associative, so the expression is (return 5 >>= f) >>= f
.
> :t return 5 >>= f
return 5 >>= f :: [Int]
Comparing this to the above type signature for (>>=)
, we can observe two things:
- The second argument of
(>>=)
must return some sort of monad - The first argument of
(>>=)
must be a value in the same monad
So we know that f
has to have a type shaped like (a -> m b)
here, and the actual type signature for f
is Int -> [Int]
. So we can manually combine the types:
a
=Int
b
=Int
m
=[]
Note that ([] a)
means the same thing as [a]
.
So the monad is indeed the list monad. So what's going on?
> return 5 :: ([] Int)
[5]
> [5] >>= f
[6, 10]
> ([5] >>= f) >>= f
[7, 12, 11, 20]
精彩评论