Deep confusion about monads
Joe English
jenglish@flightlab.com
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]
and
head :: [a] -> a
so
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:
bar
= { 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
and
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
jenglish@flightlab.com