Page 1

Page 2

# Remember the word counting function from last week?

• wordCounts :: String -> String
• putStr (wordCounts "hello clouds\nhello sky") clouds: 1 hello: 2 sky: 1
• What if we wanted to create a standalone program for counting words, instead of just testing the function in GHCi?
Page 3

• The program should have a module called Main, containing a function called
main
:
• module Main where

main :: IO ()
main = (...)

• The first line can be omitted, since the default module name is Main.
• How do we create something of type
IO ()
?
Page 4

# Creating IO actions

• The type
IO ()
represents "IO actions".
• Here are some functions (some may be familiar already) that return IO actions:
• putStr   :: String -> IO ()
print    :: Show a => a -> IO ()
interact :: (String->String) -> IO ()

• Notice that the type of
interact
fits nicely with our function
wordCounts
!
Page 5

# The program countWords

• main :: IO ()
main = interact wordCounts

wordCounts :: String -> String
wordCounts = (...) -- as before

• Compile it (assuming the file name is countWords.hs)
• ghc --make countWords.hs
• Run it (taking input from a file example.txt):
./countWords <example.txt clouds: 1 hello: 2 sky: 1
Page 6

# Pure functions

## In Haskell, functions are pure

• When a function is applied, it computes and returns a result, but nothing else happens. The are no side effects.
• As a consequence, same argument => same result. Consider
• g1 x = y + y where y = f x

g2 x = f x + f x
• g1 and g2 always compute the same result, regardless of what f is.
• g2 might be slower because of duplicated work.
• Good for modularity! Makes debugging and testing easier!
• Subexpressions can be computed in any order, even in parallel!
Page 7

# Equal and interchangeable

• In Haskell, an equation like y = f x means that we have two equal, interchangeable things, like in math!
• Whether we refer to something by name or use it directly makes no difference.
• You can use equational reasoning, like in math, to prove that functions have desired properties.
• Examples of things you could prove:
• map g . map f == map (g.f)
• map f (xs++ys) == map f xs ++ map f ys
• filter p (xs++ys) == filter p xs ++ filter p ys
• reverse (xs++ys) == reverse ys ++ reverse xs
• reverse . map f == map f . reverse
Page 8

# An example in Python

• >>> input() - input()  15  12  3 >>>
• The function input is not pure.
• It interacts with the user.
• It can return different results even if the argument it the same.
• We can tell from the result that the left operand was computed before the right operand.
Page 9

• There is actually a function similar to Python's input() in Haskell:
• getLine  hello "hello"
• And there is more:
• writeFile "hello.txt" "Hello world!"  readFile "hello.txt"  "Hello world!"
But doesn't this break purity?
Page 10

# IO and purity in Haskell

• Let's look at the types of some "impure" functions again:
• putStr    :: String -> IO ()
getLine   :: IO String
writeFile :: FilePath -> String -> IO ()
readFile  :: FilePath -> IO String

type FilePath = String
• The trick is the use of the type
IO
.
• All functions that interacts with the outside world in some way has
IO
in the result type.
• There is no way to "hide" a call to an "impure" function inside a pure function.
Page 11

# Using IO results in GHCi

• s <- readFile "hello.txt"  (s,reverse s)  ("Hello world!","!dlrow olleH")
• The left arrow <- allows us to get the result of an IO operation. Note the types
• readFile "hello.txt" :: IO String

s :: String
• The types are different!
• Haskell doesn't let you write s =
here, since it would suggest that we have two equal, interchangeable things, of the same type.
Page 12

# The do notation

• The do notation allows us to use the left arrow <- in the same way inside function definitions.
• showTheDifference :: IO ()
showTheDifference = do putStrLn "Enter two numbers:"
putStr "The difference is: "
print (x-y)

• We used these functions:
print    :: Show a => a -> IO ()
putStr   :: String -> IO ()
putStrLn :: String -> IO ()

Page 13

Page 14

# Doing IO and returning results

• What if we want a function that returns the difference instead of showing it?
• getTheDifference :: IO Integer
getTheDifference = do x <- readLn
return (x-y)

showTheDifference :: IO ()
showTheDifference = do d <- getTheDifference
putStr "The difference is: "
print d
• We need to use return:
• return :: a -> IO a
Page 15

# The difference

• One key feature of Haskell is that it is a purely functional language. Functions are pure.
• But Haskell still has "impure" functions for IO. The difference from other languages is that pure and impure functions are kept apart, by using the
IO
type to mark "impure" functions.
• You can never hide the fact that IO is used somewhere inside a large program.
• You can get access to the result of an IO operation inside a
do
block, then apply pure functions to it, but the whole
do
block has type
IO
.
Page 16

# Why pure functions?

## Purity is good for modularity and simplifies debugging and testing.

• Unexpected side effects is a major source of bugs.
• Automated testing with QuickCheck is an example where it is a good that functions are pure!
• Subexpressions can be computed in any order, even in parallel, without changing the result of the program.
Page 17

# More examples 1

copyFile :: FilePath -> FilePath -> IO ()
copyFile from to = do s <- readFile from
writeFile to s
Page 18

# More examples 2

• sortFile :: FilePath -> FilePath -> IO ()
sortFile from to = do s <- readFile from
writeFile to (sortLines s)

sortLines = unlines . sort . lines
• You probably wouldn't want to do random testing of sortFile (e.g. applying it to random file names 100 times)
• but it would make sense to test sortLines with QuickCheck.
• It's a good idea to keep most of the functions in a program pure and and keep the IO functions as small and few as possible.
Page 19

# More examples 3

• doTwice :: IO a -> IO (a,a)
doTwice io = do x <- io
y <- io
return (x,y)

don't :: IO a -> IO ()
don't io = return ()

• Try it in GHCi!
Page 20

# A larger example

## Hangman

• It's a simple word guessing game.
• See Hangman.hs