The title said it all, actually. I can't understand why this following code does not actually print "Hello World" as opposed of what >>=
does.
main = fmap putStrLn getLine
Currently, here is my line of reasoning, please check if it has any fallacy.
If we compare fmap
with >>=
(>>=) :: Monad m => m a -> (a -> m b) -> m b
fmap :: Functor f => (a -> b) -> f a -> f b
In bind, the context, or in IO terms "World" the first m
and the second m
is entirely different aside of the types. (a -> m b)
essentially recreates a new "World". This is not true in Functor, the context f
are the same hence side effects are impossible.
Now, if that's indeed the case, why doesn't the compiler gives a warning when we try to fmap
an effectful IO to an existing IO Monad?
You're almost there. What is the type of fmap putStrLn
?
putStrLn :: String -> IO ()
fmap :: Functor f => (a -> b) -> f a -> f b
fmap putStrLn :: Functor f => f String -> f (IO ())
And as a result fmap putStrLn getLine
will be IO (IO ())
, that is, an IO action, which contains another IO action. There's no need for a warning*, after all, this could be what you intended. The compiler cannot determine whether you wanted m (m a)
or m a
.
That's actually the power of a monad, it has an operation which enables you to join those actions:
join :: Monad m => m (m a) -> m a
-- join x = x >>= id
* except maybe for the missing type signature. You can tell GHC to warn you about those with -fwarn-missing-signatures
. See warnings and sanity-checking.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With