4

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?

Evan Sebastian
  • 1,714
  • 14
  • 20
  • 12
    Add `main :: IO ()` and you will notice the mistake. It is good practice to add explicit type signatures to all top-level equations. –  Jul 29 '14 at 09:48
  • 1
    A related question: http://stackoverflow.com/questions/24549610/fmap-print-value-doesnt-print-anything – Sibi Jul 29 '14 at 12:53

1 Answers1

14

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.

Zeta
  • 103,620
  • 13
  • 194
  • 236
  • An `IO` action that yields another `IO` action is normally unlikely to be the right thing, though. But as you say, this is not for the compiler to judge. – leftaroundabout Jul 29 '14 at 11:57
  • 3
    @leftaroundabout I've seen (sub-expressions) of type `IO (IO a)` arise naturally around concurrency; while a lock is held (or in some other critical section) you calculate an action to perform after the lock is released (or the critical section is left). – Boyd Stephen Smith Jr. Jul 29 '14 at 14:48
  • 1
    what about [```-fwarn-wrong-do-bind:```](https://www.haskell.org/ghc/docs/latest/html/users_guide/options-sanity.html) ? – Sebastian Wagner Jul 29 '14 at 15:04