[Haskell-beginners] Do IO actions *have* to be glued by the do syntax?

Daniel Wright dani at dpwright.com
Tue Mar 22 11:26:00 UTC 2016


Hello Olumide,

The simple answer is "no", do syntax is an entirely optional piece of syntactic sugar that is just there to make the code easier to read in some cases.  But, IO actions do have to be glued together by something, and that something is the >>= operator (pronounced "bind").

>>= takes an IO action on the left, (let's use getLine, which reads a line from the user, for our example) and a function on the right which takes the result of that action and returns the next IO action to perform.  For example:

> getLine >>= (\line -> putStrLn line)

Here we can see the getLine action on the left.  The result of that is passed, through the mechanism provided by >>=, to the function on the right, which is a lambda function taking that line as a parameter and calling the action "putStrLn" to print it back out again.  This is equivalent to the following do notation:

> do
>   line <- getLine
>   putStrLn line

What if you want to string together more than two actions, though?  For example, say we wanted to get two lines and concatenation them in the output, like so:

> do
>   line1 <- getLine
>   line2 <- getLine
>   putStrLn (line1 ++ line2)

Since >>= can only take two parameters, what do we do?  Well, since the result of >>= is itself an IO action, we can use it again in the function on the right!  Like this:

> getLine >>= (\line1 -> (getLine >>= (\line2 -> putStrLn (line1 ++ line2))))

Now, this may be starting to look a little bit like lisp with all the parentheses!  In fact, because of the associativity of the >>= operator, the brackets are all optional, but they may be helpful for you to see what's going on.  Without the brackets, it looks like this:

> getLine >>= \line1 -> getLine >>= \line2 -> putStrLn (line1 ++ line2)

Which, if you were to lay it out a little differently, looks like this:

> getLine >>= \line1 ->
> getLine >>= \line2 ->
> putStrLn (line1 ++ line2)

...Starting to look a little more like the do syntax version above, isn't it?

What if you don't care about the value of the parameter?  Well, you could ignore it using the _ syntax like this:

> getLine >>= \_ -> putStrLn "42"

But Haskell actually provides an operator to save you the effort, called >>, so you can do:

> getLine >> putStrLn "42"

Do notation is a purely syntactic transformation that emits code using these operators.

Incidentally, the function on the right doesn't have to be a lambda function!  Any function taking a value of whatever type is returned by the action on the left will do, and in the case of getLine/putStrLn they match!  So the first example could be rewritten:

> getLine >>= putStrLn

Which many would argue looks nicer than the equivalent do notation.

Hope that helps, sorry if it was a little verbose!

-Dani.

PS- I know you said not to mention monads, but if you followed everything above you're 90% of the way there anyway -- a monad is just a type class providing the >>= operator, and the "return" function, which takes a value and turns it into an action returning that value (at least in terms of the IO monad).

> On Mar 22, 2016, at 19:52, Olumide <50295 at web.de> wrote:
> 
> Hello List,
> 
> Do IO actions *have* to be glued by the do syntax? Many, if not all, the examples that I've come across in LYH seem to suggest so. And if so, why?
> 
> BTW, if possible I'd appreciate an explanation that does not resort to monads. I haven't studied them yet and I'm sure I'd struggle to understand any explanation that resorts to monads.
> 
> Thanks,
> 
> - Olumide
> _______________________________________________
> Beginners mailing list
> Beginners at haskell.org
> http://mail.haskell.org/cgi-bin/mailman/listinfo/beginners


More information about the Beginners mailing list