Deep confusion about monads

Adrian Hey ahey@iee.org
Fri, 17 Aug 2001 02:19:24 +0100


Hello,

On Thursday 16 August 2001  9:30 pm, Mark Carroll wrote:
> Let me know if I'm bugging you guys too much. It'd be great if I had
> someone close at hand who knew more about Haskell than I do, but
> unfortunately I don't. Are there any fora that are especially for people
> to help novices, or is haskell-cafe the best fit?
>
> I decided I ought to learn more about monads, so I've been trying to do
> simple IO. However, I'm running into really basic problems that I haven't
> managed to solve with the three monad tutorials I've read so far!
>
> First, given an IO String, I don't seem to be able to take the head of it
> to get an IO Char. I'm guessing that the IO monad is quite unlike the
> simple 'tainting' I first imagined; I reall do seem to have to treat it as
> an abstract polymorphic type. Worse, I can't work out how to write a IO
> String -> IO Char version of head; I can't make my (x:xs) match an IO
> String! Better would be if I could somehow write an IO String -> IO Char
> wrapper for head.
>
> 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.

Yep, won't work. As you suspected, your ideas about what IO means are
wrong. IO isn't a simple annotation to indicate an 'impure function',
and you can't use a function of type a -> IO b in the same way as you
would use a function of type a -> b.
(IO b) is an abstract data type. A *value* of type (IO b) stands for
an *action* which, when *executed* 'returns' a value of type b.
You pass the returned value to subsequent actions using the >>=
operator.

In your example..
readFile :: String -> IO String
I.E. readFile is a *function* (not an action)
so..
in x = readFile "/tmp/foo", x is an action of type (IO String).
In order actually read the file you still have to execute the action x.
Something like this should work (prints file backwards)

main :: IO ()
main = let x = readFile "/tmp/foo"  -- x is an action which will read the file
       in do foofile <- x           -- foofile is file contents
             putStr (reverse foofile)

Take a look at the Haskell report to see how 'do expressions' get
translated into monadic operators and lambda abstractions. The
above is equivalent to..
main = let x = readFile "/tmp/foo"
       in x >>= (\foofile -> putStr (reverse foofile))

> 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
>
> ...?

Yes, I think that's a typo. The second form is correct, though if you
look in the prelude you'll see it's definition applies to all monad
instances, not just the IO monad.

Regards
-- 
Adrian Hey