correct • elegant • free

△ How to do IO in Haskell △

How do you do? ▻

Hello, World!

Our first example may possibly be familiar: it's called helloworld.hs.

main = putStr "Hello, World!\n"

This is about as straightforward as it gets, but there is a lot to glean here. As promised, we will focus our attention on the types involved.

This example introduces the function putStr, which has type putStr :: String -> IO (). In words, putStr is a function which takes one argument of type String and returns a result of type IO (). So, when we apply putStr to an argument, putStr s, where s :: String, the complete expression has type IO ().

We will define an IO action to be any expression of type IO (). By this definition, putStr s is an IO action. When putStr s is evaluated, it writes s to standard output.

Now, we've happily been talking about expressions of type IO (), but that's a pretty strange looking type. Let's examine it in detail.

First of all, IO is a type constructor. This means that for any type a, there is a related type IO a. You already know about type constructors, even if you've done very little Haskell, since [] is a type constructor allowing us to define a list type for any existing type, such as [Int], a list of Ints. If you've done just a little bit more Haskell, you've probably come across type constructors such as Maybe a, which allows us to define the type of an optional value of any other type, for example Maybe String. The IO type constructor is very similar to Maybe. (There is an important difference: IO is abstract, which means there are no data constructors corresponding to the Just and Nothing constructors for Maybe types. This means that you can't simply write down a value of an IO type, nor can you pull it apart with pattern matching.)

We will soon be seeing types like IO String, but what is IO ()? You may not have come across () before, but it is indeed a type, in fact the trivial type. There is only one value of type (), which is also written (): you can think of it as a tuple with no components. The trivial type is fairly useless on its own, but IO () perfectly expresses the type of an IO action that returns no result, just like putStr.

Just to complete our discussion of types: the type of main is, the Haskell report tells us, IO a for some type a. In this case, we have instantiated a as the trivial type (), and the entire program is type correct.

Of course, we can define our own functions with an IO () result type, as in hellofred.hs.

main = hello "Fred"

hello n = putStrLn ("Hello, " ++ n ++ "!")

This example introduces the function putStrLn :: String -> IO (). When putStrLn is evaluated, it writes its String argument to standard output, followed by a newline character.

The type of hello is hello :: String -> IO (), the same as putStrLn itself.


1. Define myPutStrLn (identical to putStrLn) in terms of putStr. Solution: myputstrln0.hs.

2. Given main = birthday "Fred" 37, write the function birthday that will output the line Happy Birthday Fred, 37 years old today!. What is the type of birthday? Solution: birthday.hs.

△ How to do IO in Haskell △

How do you do? ▻