
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. 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 ...? I'm not sure if I've explained enough of my confusion that a succinct, helpful response not mentioned in what I've read so far can be easily given. 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. 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, yet I want to avoid resorting to unsafePerformIO. The really annoying thing is that all the monad examples I've read so far have made perfect sense to me. (-: What I'm really hoping is that a simple answer as to how best to arrange my IO String -> IO Char (or IO [a] -> IO a) version of head will somehow resolve a lot of my confusion. For instance, maybe I have to somehow write an 'action' version of head that I apply with the help of >>= ? -- Mark

I should add that I do seem to have written something that sort of works: firstChar :: [a] -> IO a firstChar xs = return head xs then, readFile "/tmp/foo" >>= firstChar >>= putChar ...however, I can believe that

That's embarrassing - I hit 'send' before I finished. Sorry. ): On Thu, 16 Aug 2001, Mark Carroll wrote:
I should add that I do seem to have written something that sort of works:
firstChar :: [a] -> IO a firstChar xs = return head xs
I mean firstChar xs = return $ head xs
then,
readFile "/tmp/foo" >>= firstChar >>= putChar (snip)
...what's surprising me is that do I really have to turn everything into an IO action like this just to do things with the String hidden in the IO String? -- Mark

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?
It's quite okay to ask such questions here, but you should not assume to be the first to run into these problems:-) Have a search through www.haskell.org, especially the bookshelf, the mailing-list archives for haskell and haskell-cafe, and the wiki: questions & answers (which is similar to a list of frequently asked questions, without the ordering implied by a list..). This particular question has also been raised several times on comp.lang.functional, so you can find a lot of related discussion in Google's UseNet search. You won't find all the answers you need there, and even if your specific questions have been answered there, the answers may not be helpful to you. Then, ask here, and say what resources you've tried and why they didn't help you. That way, the resources at www.haskell.org can be improved every time the questions are asked on this list.
...what's surprising me is that do I really have to turn everything into an IO action like this just to do things with the String hidden in the IO String?
Part of the answer can already be found in the wiki at www.haskell.org, but as you say you've tried some monad tutorials, here goes another longish explanation attempt: There is no String hidden in an IO String (at least, there need not be one). If you have a function f :: String -> String, there need not be a String hidden in f -- a call to f could just give you back something constructed from its parameter. So f promises a String when passed a String, and the only way to get at that result String is by applying f to a String. If you have i :: IO String, the situation is similar: - f is a function with an explicit parameter and calling this function returns a String - i is an IO-action, with implicit access to an IO-environment, and executing it may do things to the IO-environment, and will produce a String In both cases, you've got a promise of a String, but not necessarily a String. The difference is that f only has access to its definition and its parameter, and it only returns a String, so you can use it in any context that supplies a String parameter and expects a String result. In contrast, i also wants access to an IO-environment, and it returns a String and may modify the IO-environment, so you can use it in any context that supplies access to an IO-environment and expects a String and (potential) changes to that IO-environment. With this background, your question is easily answered: an IO String action only promises a String, and to get that String you have to execute the action in an IO-environment. You can't do that inside an expression that isn't of type IO something, because expressions that are not of that type shouldn't have access to an IO-environment (they may pass IO actions around, but they can't execute them). So, you don't need to convert everything to an IO action to do something with the String, but you need to be able to execute the IO action that promises the String. And you can't embed that IO action in a non-IO expression, so your overall programm will be an IO action:-( However, you can embed functional expressions in an IO action:-) And that's just a complicated way to describe what you've already discovered: f :: String -> Char {- no IO here, and f could be an arbitrarily complex functional computation -} f s = head s i :: IO String {- if given access to an IO-environment, this should produce a String -} i = readFile "input" (i >>= (\s-> return (f s))) >>= putChar {- or: do { s <- i; c<- return (f s); putChar c } or: do { s <- i; putChar (f s) } -} The "return" embeds an arbitrary expression into an IO-action that does not access its IO-environment (as far as that environment is concerned, it is a null action). And the "s<- i; return (f s)" part binds s to the String returned by i *and* it composes the effects that i and "return (f s)" might have on the IO-environment. That's why you can't simply use a let-binding instead of the monadic binding: let doesn't know about that extra IO-environment. Or, in monad-speak: function application and let-binding take place in the identity monad (the monad which doesn't add anything extra). IO actions and their bindings take place in the IO monad (the monad that adds access to IO-environments to functional computations). In contrast to other monads, such as List, MayBe, .., you won't find an operation of type M a -> a if M is the IO monad (guess what, you will, but it's unsafe;-). The reason is that other agents observe the IO-environment, so changes to it won't go unnoticed (you can throw away the evidence that you really had a list of results instead of just a single result, but you can't throw away the evidence that you've reached outside your functional program..). This brings us back to your idea of "tainting": not the Strings themselves are tainted (they are as pure as anything else), the computation that produces the String is tainted if it needs IO to produce a String. For "untainted", purely functional String computations, there is no difference (apart from resource usage) between the computations and the Strings they produce, but for "IO-tainted" computations, there is such a difference. Anyone still reading?-) If yes, and if it should have been helpful, perhaps someone could condense this and add it to the wiki? Claus PS. Once Upon A Long Ago, I tried to put some of the various functional IO schemes into a logical development (a kind of "design proof" or "design derivation").. a bit dated, and not necessarily helpful to those currently struggling with IO in Haskell, but perhaps of historical interest?-) Those who like that kind of thing can find it in chapter 3 of someone's thesis: http://www.cs.ukc.ac.uk/people/staff/cr3/publications/phd.html

Hello again, On Thursday 16 August 2001 9:39 pm, Mark Carroll wrote:
That's embarrassing - I hit 'send' before I finished. Sorry. ):
On Thu, 16 Aug 2001, Mark Carroll wrote:
I should add that I do seem to have written something that sort of works:
firstChar :: [a] -> IO a firstChar xs = return head xs
I mean firstChar xs = return $ head xs
then,
readFile "/tmp/foo" >>= firstChar >>= putChar
(snip)
...what's surprising me is that do I really have to turn everything into an IO action like this just to do things with the String hidden in the IO String?
No, you could write instead.. (readFile "/tmp/foo") >>= (\foofile -> putChar (head foofile)) or.. (readFile "/tmp/foo") >>= (putChar . head) HTH Regards -- Adrian Hey

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

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
participants (4)
-
Adrian Hey
-
Claus Reinke
-
Joe English
-
Mark Carroll