• No results found

The IO Monad and ’do’ notation

The IO Monad in Haskell is a monad defined in Haskell containing many standard I/O functions. As we have already seen, monads are used in Haskell to deal with side-effects, and as such it is only natural for I/O to take place within a monadic structure. There are many I/O functions defined within the IO Monad so we shall only take a look at a few here, but much more information is available on-line

at the Haskell homepage. As an aside it is interesting to note that writing the standard “Hello World” program in Haskell requires the use of the IO Monad (see figure 4.1). The “Hello World” program has the type IO () as it doesn’t return any result, but has the side effect of putting the string “Hello, World!” to the standard output. (The function putStrLn :: String → IO () is defined as part of the IO Monad.)

main :: IO ()

main = putStrLn "Hello, World!"

Figure 4.1: “Hello World” written in Haskell

We have seen previously, that monadic operations in Haskell are defined with a bind (>>=) function, and the return function. This is also the case for the IO Monad. If we wish to compose functions from the IO Monad then we need to make use of the bind operation, and if we wish to return a result within the IO Monad then we must make use of the return function. For example, if we wanted to define a function that prompts a user for their name, and then outputs a string that welcomes the user, we would have to bind together the two occurrences of putStrLn :: String → IO () and the function (defined as part of the IO Monad) that reads in a string from the standard input (getLine :: IO String) as follows:

welcome :: IO ()

welcome = putStrLn "Please enter your name:" >>= λ → getLine

>>= λname → putStrLn ("Hello, " ++ name)

I have laid out the code in quite a readable manner, but as these monadic functions become longer and longer, it is often easier to think of them in an imperative manner. E.g. within the monadic computation we can bind results from other monadic operations to variables, and then use these results in later operations within the overall monadic computation. Haskell provides us with the ’do’ notation for this purpose. It is in fact just syntactic sugar, and is converted back into the monadic binds at compile time, but it enables the user to have a more imperative

style when writing monadic programs. It can be used for any monad defined in Haskell, which is not only useful here for our examples of using the IO Monad, but also later in this thesis when we define the Quantum IO Monad in Haskell (See chapter 5). As a first introduction to ’do’ notation we can simply re-write the previous example (welcome) using it as follows:

welcome′:: IO ()

welcome′ = do putStrLn "Please enter your name:"

name ← getLine

putStrLn ("Hello, " ++ name)

The number of functions available in the IO Monad is quite large, and therefore it is not realistic to look into all the implementations of the functions. Because of this, the IO Monad is usually introduced in terms of the I/O functions it provides, or more abstractly just as an interface to I/O computations in Haskell. In Chapter 5 I shall also take this approach for introducing the QIO Monad as an interface to quantum computations in Haskell, although I shall go on to describe the implementation of the QIO Monad later in Chapter 7. For the rest of this section, I shall introduce a few more common I/O functions that are provided in the IO Monad, and give some examples of monadic programs written in ’do’ notation that make use of them.

One such use of the IO Monad, which we shall be using in our implementation of the QIO Monad, is to enable the use of a random number generator. A random number generator is obviously an impure function as we wouldn’t want it to return the same value every time it was called. In Haskell, it is possible to create many random number generators, but for this example we shall make use of the “global” random number generator that sits in the IO Monad. There is a type class provided in the library System.Random called Random, and any instance of this type class (a) must provide a function randomIO :: IO a, which returns a random element of the given type, and a function randomRIO :: (a, a) → IO a which returns a element of the given type that is in the range given by the argument pair. Some

common instances of the Random class are booleans, integers, characters, and floating point numbers. As an example we could write a short dice playing game, whereby a user enters their name and tries to throw a 6. (Note that we would have to first import the necessary System.Random library.)

diceGame :: IO ()

diceGame = do putStrLn "Please enter your name: " name ← getLine

diceGame′ name diceGame′:: String → IO ()

diceGame′ name =

do putStrLn "Press any key to roll the dice..." getChar

x ← randomRIO (1 :: Int, 6)

putStr ("\b" ++ "You threw a " ++ show x ++ "... ") if x ≡ 6 then do putStrLn "You Win!"

putStrLn ("Thank you, " ++ name) else doputStrLn "You Lose!"

diceGame′ name

Calling the diceGame function will prompt the user for their name and pass the given name to the diceGame′ function. The diceGameuses the random number

generator to simulate the throwing of a dice by returning a random number (in- teger) in the range of 1 to 6. The game repeats until the user has thrown a 6, at which point they have won and the function call can exit. This is a nice example of writing monadic programs in the IO monad, and also shows how the ’do’ notation gives our effectful programs are more imperative look.

Another use of the IO Monad (and monads in general) is to enable stateful programs, for example, programs that require mutable data and/or references to data stored in memory. In Haskell, we have IORef s to enable the use of mutable references in the IO Monad. It is also useful at this point to mention that there

are other monads which are provided in the standard libraries for similar uses, such as the State monad which we introduced previously.