Deep confusion about monads

Joe English
Thu, 16 Aug 2001 19:26:05 -0700

Mark Carroll wrote:

> First, given an IO String, I don't seem to be able to take the head of it
> to get an IO Char.

The bit of plumbing you're looking for is:

    fmap :: Functor f => (a -> b) -> (f a -> f c)

Every Monad worthy of the name is also a Functor,
so fmap works for IO too.  Here's how:

You've got
	foo :: IO [Char]
	head :: [a] -> a
	fmap head :: (Functor f) => f [a] -> f A

can be applied to foo to give

	fmap head foo :: IO Char

(You can't of course take the head of an IO String; that
wouldn't make sense.)

You could also start with:

    bar :: IO Char
    bar = do
	theString <- foo
	return (head theString)

and simplify:

  = { definition }
    do theString <- foo ; return (head theString)
  = { translate 'do' syntax }
    foo >>= \ theString -> return (head theString)
  = { f (g x) == (f . g) x }
    foo >>= \ theString -> (return . head) theString
  = { eta-reduce; (\x -> f x) ==> f }
    foo >>= return . head
  = { Monad law #4 in Haskell report (section 6.3.6 "Class Monad") }
    fmap head foo

> I'm also confused as to why I can write:
> readFile "/tmp/foo" >>= putStr
> to print out the contents of a file but, in ghci,
> let x = readFile "/tmp/foo"
> putStr x
> ...doesn't work.

That's because you have

	x :: IO String
	putStr :: String -> IO ()

The types don't match.

> Then again, I'm also confused by the way that Simon
> Thompson's book says that,
> (>>=) :: IO a -> (a -> IO a) -> IO a
> which I think might be a misprint for,
> (>>=) :: IO a -> (a -> IO b) -> IO b

Quite right.  Actually the type is a even more general --
it's (>>=) :: (Monad m) => m a -> (a -> m b) -> m b
It works for any Monad, not just I/O.

> I guess that my problem is that I had initially imagined that the
> prefix 'IO' appears as a magical tainting of any data that could depend on
> the result of some IO action. However, I'm coming to the conclusion that
> I'm quite wrong in this.

That's actually a fairly useful way of looking at the IO monad
in my opinion.

> I'd like to be able to manipulate IO Strings as
> if they were just strings that I can't forget came from IO, but I'm
> guessing now that things aren't that simple - they really are quite
> different to strings

The trick is to just write functions that operate on Strings,
and use 'fmap' (and other combinators) to turn them into
functions that work on IO Strings.

--Joe English