tobold.org

correct • elegant • free

△ How to do IO in Haskell △

◅ Hello, World!

More output functions ▻

How do you do?

Let's make things just a tiny bit more complicated with howdo.hs.

main = do
  putStrLn "Hello"
  putStrLn "How do you do?"

This is our first encounter with do, which plays a starring role in this tutorial: we will be exploring exactly what do means and what you can do with it. It's easy to get the idea that do is a magic incantation which means "perform some IO now", but that idea is mistaken. After all, the examples so far have happily performed IO, yet never mentioned do. We'll firm up our ideas of what do means as we go along.

This example shows one of the three things that can go inside a do expression: an IO action. In fact, we have two IO actions here, and the do construct can be read as "perform this IO action, and then perform this IO action". The notion of sequence is important here; normally Haskell doesn't say much about when expressions will be evaluated, but inside a do expression they are evaluated in order.

So our initial understanding of do is that it evaluates, in order, the IO actions it contains.

As already mentioned, the do construction is an expression, so - like every Haskell expression - it has a type. The type of the do expression is the type of the last IO action it contains: in this case our old friend IO ().

We can define our own IO actions (expressions of type IO ()) and use them inside a do expression, as in hello-bye.hs.

main = do
  hello "Fred"
  goodbye

hello n = putStrLn ("Hello " ++ n ++ "...")
goodbye = putStrLn "...and Goodbye."

Definitions of IO actions do not have to be separate top-level functions: they can also occur, like any definition, inside let expressions and where clauses. Which of these to use is largely a matter of style. The hibye.hs example demonstrates all the possibilities.

main =
    let bye = putStrLn "Bye!" in
    do
      hi "Fred"; how; bye
    where
      hi n = putStrLn ("Hi " ++ n)

how = putStrLn "How're ya doin'?"

This example also demonstrates that normal layout rules apply to do expressions: you can separate IO actions either by putting them on separate lines (suitably indented), or with a semi-colon ;.

Exercise

3. Define myPutStrLn again, but this time with a do expression. Solution: myputstrln1.hs.

△ How to do IO in Haskell △

◅ Hello, World!

More output functions ▻