
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