Lecture 23-2
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
throughvk
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 theelse
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 =>
substituteIO
form
. - So
return :: a -> IO a
You don’t need to know anything about monads for COMP105.
Exercises
-
doubleEcho :: IO () doubleEcho = do x <- getLine putStrLn x putStrLn x
-
firstWord :: IO () firstWord = do string <- getLine if (words string) == [] then putStrLn "" else putStrLn (head . words $ string)
-
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.