Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

fmap putStrLn getLine does not perform IO

Tags:

haskell

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?

like image 545
Evan Sebastian Avatar asked Dec 08 '22 07:12

Evan Sebastian


1 Answers

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.

like image 74
Zeta Avatar answered Dec 17 '22 14:12

Zeta