Reputation: 41939
Consider the following IO
code:
ghci> let x = return 100 :: IO Int
ghci> :t do { a <- x; print a; return 500 }
do { a <- x; print a; return 500 } :: Num b => IO b
My understanding of do notation/bind is that the following signature will be enforced by the compiler:
ghci> :t (>>=)
(>>=) :: Monad m => m a -> (a -> m b) -> m b
In the above example, my understanding is:
x
has type IO Int
print a
has type IO ()
return 500
has type Num b => IO b
It seems to me that IO ()
does not conform to the type, Num b => IO b
. Additionally, IO Int
does not conform to Num b => IO b
, as I understand.
If this observation is valid, then why does this code compile? Must not each line, i.e. >>=
, conform to m b
, where m
equals IO and b
equals Num b => b
?
Upvotes: 0
Views: 113
Reputation: 32329
The code you posted desugars into the following.
x >>= (\a ->
print a >>
return 500)
Or, expanding out the definition of (>>)
x >>= (\a ->
print a >>= (\_ ->
return 500))
Then, you can see that in the different calls to (>>=)
, the types a
and b
are not necessarily the same. Say (>>=) :: Monad m => m a -> (a -> m b) -> m b
.
in the first call: x
has type IO Int
, \a -> print a >>= (\_ -> return 500)
has type Num c => Int -> IO c
, so the a
in our type signature for (>>=)
is Int
, and the b
is c
(with the Num
restriction).
in the second call: print a
has type IO ()
, and \_ -> return 500
has type Num c => () -> IO c
(the ()
part is inferred from trying to match the signature of (>>=)
) so the a
in our type signature for (>>=)
is ()
, and the b
is c
(still with the Num
restriction).
Upvotes: 3
Reputation: 21757
do { a <- x; print a; return 500 }
is equivalent to (return 100 :: Int) >>= print >> return 500
.
The signature for >>
is (>>) :: Monad m => m a -> m b -> m b
, which is in sync with what is seen.
Upvotes: 1