tobold.org

correct • elegant • free

△ How to do IO in Haskell △

◅ Input, binding

Actions versus computations ▻

return is not control-flow syntax

If you have a background in procedural languages, you will probably find the behaviour of this example, return0.hs, surprising.

main = stuff

stuff = do
  putStrLn "hello"
  return 5
  putStrLn "goodbye"

Both strings are output! Doesn't the return statement cause an early return from stuff?

No, because there is no return statement in Haskell. It's a return expression. In this case, the return expression produces a value of type IO Int, which is immediately discarded. It has no effect on how (or whether) the remainder of the do expression is evaluated.

So if return is not control-flow syntax, what is it for? As we have already seen, return usually appears at the end of a do expression, because the value of a do expression is the value of the last expression it contains.

There is another use of return: when the language requires an IO action, but you don't actually want to perform any IO. Indeed, perhaps the best way to see return () is as an IO action that does no IO! Our next example, return1.hs demonstrates this.

main = do
  putStr "How old are you? "
  x <- getLine
  let age = read x
  if age > 99
  then putStrLn "My, that's old!"
  else return ()
  if age < 10
  then putStrLn "Never too young to learn Haskell!"
  else return ()
  putStrLn ("Nice age to be, " ++ show age)

In Haskell, if always comes with an else branch, which must have the same type as the then branch. In this case, we don't want the else branch to do anything, but it needs to have type IO () to match putStrLn, so return () is ideal. (We'll see a neater way to handle this situation later.) As you should expect by now, the Nice age... message is always output.

Incidentally, you might be tempted to try to eliminate the name x in this example by writing age <- read (getLine). That won't work, because read needs a String argument, while getLine returns a value of type IO String. The binding with <- extracts that string from its IO encumbrance.

△ How to do IO in Haskell △

◅ Input, binding

Actions versus computations ▻