Suppose for a moment that the IO were just a Functor , not a Monad . How can we do two things? Let's say getChar :: IO Char and putChar :: Char -> IO () .
We could try matching on getChar (an action that reads Char from stdin when executed) using putChar .
fmap putChar getChar :: IO (IO ())
Now we have a program that, when executed, reads Char from stdin and creates a program that, when executed, writes Char to stdout. But what we really want is a program that, when executed, reads Char from stdin and writes Char to stdout. Therefore, we need a โflatteningโ (in the case of an IO โsequenceโ) with a type:
join :: IO (IO ()) -> IO ()
Functor alone does not provide this feature. But this is a function from Monad , where it has a more general type:
join :: Monad m => m (ma) -> ma
What does all this have to do with >>= ? As it happens, monadic binding is just a combination of fmap and join :
:t \mf -> join (fmap fm) (Monad m) => m a1 -> (a1 -> ma) -> ma
Another way to see the difference is that fmap never changes the overall structure of the displayed value, but join (and therefore >>= ) can do this.
In terms of IO actions, fmap will never cause additional reads / writes or other effects. But join does the read / write sequence of the internal action after the external action.
danidiaz
source share