Skip to content
UoL CS Notes

Lecture 23-2

COMP105 Lectures

Writing IO Code

We can write our own IO actions:

print_two :: String -> String -> IO ()
Print_two s1 s2 = putStrLn (s1 ++ s2)
> print_two "abc" "def"
abcdef

Note that the return type is IO ().

Combining Multiple IO Calls

The do syntax allows us to combine multiple IO actions.

get_and_print :: IO ()
get_and_print =
    do
        x <- getLine
        y <- getLine
        putStrLn (x ++ " " ++ y)

This syntax allows us two write a sequence of statements that will be executed in order.

You could also write this with the following syntax:

get_and_print :: IO ()
get_and_print = do
    x <- getLine
    y <- getLine
    putStrLn (x ++ " " ++ y)

The do Syntax

A do block has the following syntax:

do
    v1 <- [IO action]
    v2 <- [IO action]
    ...
    vk <- [IO action]
    [IO action]
  • v1 through vk unbox the results of IO actions.
  • The final IO action is the return value.

The v <- portion can be skipped if you are using an IO action purely for the side effect.

let in do Blocks

let expressions can be used inside do block in order to complete pure computations:

add_one :: IO ()
add_one =
    do
        n <- getLine
        let num = (read n) :: Int
            out = show (num + 1)
        putStrLn out

In a do block you can type anything out that you would type into the command line of GHCI.

if in do Blocks

guess :: IO ()
guess = do
    x <- getLine
    if x == "42"
        then putStrLn "Correct!"
        else putStrLn "You are very wrong."

Both branches of the if mut have the same type.

do Blocks

do blocks let you sequence multiple actions:

  • Works with IO actions.
  • Will not work in pure functional code.

Functional programs consist of:

  • A small amount of IO code.
  • A large amount of pure functional node.

Don’t try to write your entire program in IO code.

Putting Values in the IO Box

Sometimes we need to pus a pure value into IO.

  • We can use the return function to do this.
> :t "hello"
"hello" :: [Char]

> :t return "hello"
IO [Char]
example :: IO String
example = do
    x <- getLine
    return (tail x)

You must use return to get the value out of an impure function.

print_if_short :: String -> IO ()
print_if_short str =
    if length str <= 2
        then putStrLn str
        else return ()

Both sides of the if must have the type IO ():

  • So we use return () in the else part.

return

This function is not the same as in imperative languages return does not stop execution. It just convert pure values to IO values.

Monad

The type of return mentions monads.

> :t return
return :: Monad m => a -> m a

This is because IO is a monad.

  • Whenever you see Monad m => substitute IO for m.
  • So return :: a -> IO a

You don’t need to know anything about monads for COMP105.

Exercises

  1.  doubleEcho :: IO ()
     doubleEcho = do
         x <- getLine
         putStrLn x
         putStrLn x
    
  2.  firstWord :: IO ()
     firstWord = do
         string <- getLine
         if (words string) == []
             then putStrLn ""
             else putStrLn (head . words $ string)
    
  3.  printEven :: Int -> IO ()
     printEven x = if (even x) 
         then (putStrLn x)
         else return ()
    

    This doesn’t need to be done in a do block as it is just one line.