24
© M. Winter COSC 4P41 – Functional Programming 9.1 Programming with actions Why is I/O an issue? I/O is a kind of side-effect. Example: Suppose there is an operation inputInt :: Int -- reads an integer inputDiff = inputInt – inputInt inputInt should return two different values (depending on the users input). Hence, its evaluation depends on the context where (or when) it is executed side-effect!!! The meaning of an expression relying on side-effects is no longer determined by looking only at the meanings of its parts.

© M. Winter COSC 4P41 – Functional Programming 9.19.1 Programming with actions Why is I/O an issue? I/O is a kind of side-effect. Example: Suppose there

Embed Size (px)

Citation preview

Page 1: © M. Winter COSC 4P41 – Functional Programming 9.19.1 Programming with actions Why is I/O an issue? I/O is a kind of side-effect. Example: Suppose there

© M. Winter

COSC 4P41 – Functional Programming

9.1

Programming with actions

Why is I/O an issue?

• I/O is a kind of side-effect. Example:Suppose there is an operationinputInt :: Int -- reads an integer

inputDiff = inputInt – inputInt

inputInt should return two different values (depending on the users input). Hence, its evaluation depends on the context where (or when) it is executed side-effect!!!

• The meaning of an expression relying on side-effects is no longer determined by looking only at the meanings of its parts.

Page 2: © M. Winter COSC 4P41 – Functional Programming 9.19.1 Programming with actions Why is I/O an issue? I/O is a kind of side-effect. Example: Suppose there

© M. Winter

COSC 4P41 – Functional Programming

9.2

Monadic I/O

• The Haskell type IO a is the type of I/O actions of type a, e.g., the elements of a are somehow wrapped into an I/O container; the monad IO.

• An expression of type IO a is a program which will do some I/O and then return a value of type a.

• One way of looking at the I/O a types is that they provide a simple imperative language for writing I/O programs on top of Haskell, without compromising the functional model of Haskell.

• This approach is called the monadic approach. It is more general and can be used to incorporate side-effect into a pure functional programming language (without its problems).

Page 3: © M. Winter COSC 4P41 – Functional Programming 9.19.1 Programming with actions Why is I/O an issue? I/O is a kind of side-effect. Example: Suppose there

© M. Winter

COSC 4P41 – Functional Programming

9.3

Reading input and writing output

getLine :: IO String

getChar :: IO Char

putStr :: String -> IO ()

print :: Show a => a -> IO ()

where () is the type containing exactly one element; the element ().

Remark:In WinGHCi just those I/O actions of type IO () are actually

printed tothe screen.

Page 4: © M. Winter COSC 4P41 – Functional Programming 9.19.1 Programming with actions Why is I/O an issue? I/O is a kind of side-effect. Example: Suppose there

© M. Winter

COSC 4P41 – Functional Programming

9.4

The do notation

The do notation is a flexible mechanism. With respect to the monad IO

it supports two things:• it is used to sequence I/O programs, and• it is used to ‘capture’ the values returned by IO actions and

so to pass these values to actions which follow them in the program.

In general:• it is used to sequence programs wrapped in monad, and• it is used to unwrap the values returned by the monad and so

to pass these values to actions which follow them in the program.

Page 5: © M. Winter COSC 4P41 – Functional Programming 9.19.1 Programming with actions Why is I/O an issue? I/O is a kind of side-effect. Example: Suppose there

© M. Winter

COSC 4P41 – Functional Programming

9.5

Examples

putStrLn :: String -> IO ()

putStrLn str = do putStr str

putStr ”\n”

read2lines :: IO ()

read2lines = do getLine

getLine

puStrLn ”Two lines read.”

getNput :: IO ()

getNput = do line <- getLine

putStrLn line

where line names the result of getLine (local variable).

Page 6: © M. Winter COSC 4P41 – Functional Programming 9.19.1 Programming with actions Why is I/O an issue? I/O is a kind of side-effect. Example: Suppose there

© M. Winter

COSC 4P41 – Functional Programming

9.6

Examples (cont’d)

reverse2lines :: IO ()

reverse2lines = do line1 <- getLine

line2 <- getLine

let rev1 = reverse line1

let rev2 = reverse line2

putStrLn rev1

putStrLn rev2

getInt :: IO Int

getInt = do line <- getLine

return (read line :: Int)

where return :: a -> IO a packs an a value in the IO monad.

Page 7: © M. Winter COSC 4P41 – Functional Programming 9.19.1 Programming with actions Why is I/O an issue? I/O is a kind of side-effect. Example: Suppose there

© M. Winter

COSC 4P41 – Functional Programming

9.7

Examples (cont’d)

Notice, that the following program is not type correct:

getInt = do line <- getLine

(read line :: Int)

Each component of a do construction has to be an element of IO a for a

type a.

while :: IO Bool -> IO () -> IO ()

while test action

= do res <- test

if res then do action

while test action

else return ()

Page 8: © M. Winter COSC 4P41 – Functional Programming 9.19.1 Programming with actions Why is I/O an issue? I/O is a kind of side-effect. Example: Suppose there

© M. Winter

COSC 4P41 – Functional Programming

9.8

Examples (cont’d)

isEOF :: IO Bool

copyInputToOutput :: IO ()

copyInputToOutput

= while (do res <- isEOF

return (not res))

(do line <- getLine

putStrLn line)

Page 9: © M. Winter COSC 4P41 – Functional Programming 9.19.1 Programming with actions Why is I/O an issue? I/O is a kind of side-effect. Example: Suppose there

© M. Winter

COSC 4P41 – Functional Programming

9.9

Examples (cont’d)

goUntilEmpty :: IO ()goUntilEmpty = do line <- getLine if line == [] then return () else (do putStrLn line goUntilEmpty)

goUntilEmpty’ :: IO ()goUntilEmpty’ = do line <- getLine while (return (line /= [])) (do putStrLn line line <- getLine return ())

does not work since variables cannot be updated.

here we create a new variable

Page 10: © M. Winter COSC 4P41 – Functional Programming 9.19.1 Programming with actions Why is I/O an issue? I/O is a kind of side-effect. Example: Suppose there

© M. Winter

COSC 4P41 – Functional Programming

9.10

Calculator example

data Expr = Lit Int | Var Var | Op Ops Expr Expr

data Ops = Add | Sub | Mul | Div | Mod

type Var = Char

initial :: Store

value :: Store -> Var -> Int

update :: Store -> Var -> Int -> Store

data Command = Eval Expr | Assign Var Expr | Null

commLine :: String -> Command

eval :: Expr -> Store -> Int

Page 11: © M. Winter COSC 4P41 – Functional Programming 9.19.1 Programming with actions Why is I/O an issue? I/O is a kind of side-effect. Example: Suppose there

© M. Winter

COSC 4P41 – Functional Programming

9.11

Calculator example (cont’d)

command :: Command -> Store -> (Int,Store)

command Null st = (0 , st)

command (Eval e) st = (eval e st , st)

command (Assign v e) st = (val , newSt)

where val = eval e st

newSt = update st v val

calcStep :: Store -> IO Store

calcStep st

= do line <- getLine

comm <- return (commLine line)

(val , newSt) <- return (command comm st)

print val

return newSt

Page 12: © M. Winter COSC 4P41 – Functional Programming 9.19.1 Programming with actions Why is I/O an issue? I/O is a kind of side-effect. Example: Suppose there

© M. Winter

COSC 4P41 – Functional Programming

9.12

Calculator example (cont’d)

calcSteps :: Store -> IO ()

calcSteps st = while notEOF

(do newSt <- calcStep st

calcSteps newSt)

notEOF :: IO Bool

notEOF = do res <- isEOF

return (not res)

mainCalc :: IO ()

mainCalc = calcSteps initial

Page 13: © M. Winter COSC 4P41 – Functional Programming 9.19.1 Programming with actions Why is I/O an issue? I/O is a kind of side-effect. Example: Suppose there

© M. Winter

COSC 4P41 – Functional Programming

9.13

Further I/O

• File I/O– readFile :: FilePath -> IO String– writeFile :: FilePath -> String -> IO ()– appendFile :: FilePath -> String -> IO ()

• Errors– ioError :: IOError -> IO a– catch :: IO a -> (IOError -> IO a) -> IO a

where IOError is the system-dependent data type of I/O errors. More on

error handling (especially the type IOError) can be found in the documentation for the I/O library IO.hs.

Page 14: © M. Winter COSC 4P41 – Functional Programming 9.19.1 Programming with actions Why is I/O an issue? I/O is a kind of side-effect. Example: Suppose there

© M. Winter

COSC 4P41 – Functional Programming

9.14

Monads

IO is a type constructor, i.e., an operation on types. It maps a type a to the

type IO a of I/O action on a.

A characteristic of the I/O monad is that it allows to sequence operations.

do line <- getLine putStrLn line

What is the type of such a combinator which sequences the operations:

(>>=) :: IO a -> (a -> IO b) -> IO b

or, more general, for an arbitrary type constructor m, a combinator

(>>=) :: m a -> (a -> m b) -> m b

Page 15: © M. Winter COSC 4P41 – Functional Programming 9.19.1 Programming with actions Why is I/O an issue? I/O is a kind of side-effect. Example: Suppose there

© M. Winter

COSC 4P41 – Functional Programming

9.15

Monads (cont’d)class Monad m where

(>>=) :: m a -> (a -> m b) -> m breturn :: a -> m a(>>) :: m a -> m b -> m b

fail :: String -> m am >> k = m >>= \_ -> kfail s = error s

Requirements (informally):• The operation return x should simply return the value x,

without any additional computational effect.• The sequencing given by >>= should be irrelevant of the way

that expressions are bracketed.• >> acts like >>=, except that the value returned by the first

expression is discarded rather than being passed to the second argument.

• The value fail s corresponds to a computation that fails, giving the error message s.

Page 16: © M. Winter COSC 4P41 – Functional Programming 9.19.1 Programming with actions Why is I/O an issue? I/O is a kind of side-effect. Example: Suppose there

© M. Winter

COSC 4P41 – Functional Programming

9.16

Rules for monads

(>@>) :: Monad m => (a -> m b) -> (b -> m c) -> (a -> mc)

f >@> g = \x -> (f x) >>= g

Rules that should be satisfied:(M1) return >@> f = f identity law (left)(M2) f >@> return = f identity law

(right)(M3) (f >@> g) >@> h = f >@> (g >@> h) associativity

In terms of (>>=):(M1) (return x) >>= f = f x

(M2) m >>= return = m

(M3) (m >>= f) >>= g = m >>= (\x -> (f x) >>= g)

In category theory (>@>) is called the Kleisli composition.

Page 17: © M. Winter COSC 4P41 – Functional Programming 9.19.1 Programming with actions Why is I/O an issue? I/O is a kind of side-effect. Example: Suppose there

© M. Winter

COSC 4P41 – Functional Programming

9.17

The do notation revisited

The do notation can be used for arbitrary monads (not just for the I/O monad). It just a shorthand for an expression build up from (>>=)

and (>>).

Example:

do line <- getLine putStrLn line putStrLn ”That’s it.” return 5

is translated to

getLine >>= putStrLn >> putStrLn ”That’s it.”>> return 5

Page 18: © M. Winter COSC 4P41 – Functional Programming 9.19.1 Programming with actions Why is I/O an issue? I/O is a kind of side-effect. Example: Suppose there

© M. Winter

COSC 4P41 – Functional Programming

9.18

Examples of monads

• Identity monad– m a = a– m >>= f = f m– return = id

• I/O monad• List monad

instance Monad [] wherexs >>= f = concat (map f xs)return x = [x]fail s = []

• Maybe monad (Error monad)instance Monad Maybe where

(Just x) >>= k = k xNothing >>= k = Nothingreturn = Justfail s = Nothing

Page 19: © M. Winter COSC 4P41 – Functional Programming 9.19.1 Programming with actions Why is I/O an issue? I/O is a kind of side-effect. Example: Suppose there

© M. Winter

COSC 4P41 – Functional Programming

9.19

Examples of monads (cont’d)

• Parsing monad (Parsec – a useful parser combinator library)– Import statement: import

Text.ParserCombinators.Parsec

The type GenParser a state b• is the type of parsers taking lists of a elements as input

producing an element of b.• is implemented similar to the type Parser from Week 7.• has a parameter state. This parameter can be used as an

internal state storing information during the parsing process.• has an internal and hidden error state. This state is used to

keep track of errors that occurred during the parsing process.GenParser combines 3 monadic structures (parser monad, state

monad, error monad) into one data structure.

Page 20: © M. Winter COSC 4P41 – Functional Programming 9.19.1 Programming with actions Why is I/O an issue? I/O is a kind of side-effect. Example: Suppose there

© M. Winter

COSC 4P41 – Functional Programming

9.20

Examples of monads (cont’d)

• State monad– a state monad is a type of the form State a b = a ->

(a,b) where the type constructor is State a.– An operation of this type can change the state (of type a)

before returning a value of type b.– Example:

data Tree a = Nil | Node a (Tree a) (Tree a)

Moon

Dweezil

Ahmet

Ahmet

Moon

0

2

1

1

0

Page 21: © M. Winter COSC 4P41 – Functional Programming 9.19.1 Programming with actions Why is I/O an issue? I/O is a kind of side-effect. Example: Suppose there

© M. Winter

COSC 4P41 – Functional Programming

9.21

Examples of monads (cont’d)

type Table a = [a]

data State a b = State (Table a -> (Table a , b))

instance Monad (State a) where

return x = State (\tab -> (tab,x))

(State st) >>= f = State (\tab -> let

(newTab,y) = st tab

(State trans) = f y

in trans newTab)

Page 22: © M. Winter COSC 4P41 – Functional Programming 9.19.1 Programming with actions Why is I/O an issue? I/O is a kind of side-effect. Example: Suppose there

© M. Winter

COSC 4P41 – Functional Programming

9.22

Examples of monads (cont’d)

numberTree :: Eq a => Tree a -> State a (Tree Int)

numberTree Nil = return Nil

numberTree (Node x t1 t2) = do num <- numberNode x

nt1 <- numberTree t1

nt2 <- numberTree t2

return (Node num nt1 nt2)

numberNode :: Eq a => a -> State a Int

numberNode x = State (nNode x)

nNode :: Eq a => a -> (Table a -> (Table a , Int))

nNode x table | elem x table = (table , lookup x table)

| otherwise = (table++[x] , length table)

Page 23: © M. Winter COSC 4P41 – Functional Programming 9.19.1 Programming with actions Why is I/O an issue? I/O is a kind of side-effect. Example: Suppose there

© M. Winter

COSC 4P41 – Functional Programming

9.23

Examples of monads (cont’d)

extract :: State a b -> b

extract (State st) = snd (st [])

numTree :: Eq a => Tree a => Tree Int

numTree = extract . numberTree

Page 24: © M. Winter COSC 4P41 – Functional Programming 9.19.1 Programming with actions Why is I/O an issue? I/O is a kind of side-effect. Example: Suppose there

© M. Winter

COSC 4P41 – Functional Programming

9.24

Some Monad Functions

Module Prelude

• readFile :: FilePath -> IO String• sequence :: Monad m => [m a] -> m [a] • sequence_ :: Monad m => [m a] -> m () • mapM :: Monad m => (a -> m b) -> [a] -> m [b] • mapM_ :: Monad m => (a -> m b) -> [a] -> m ()

Module Data.IORef (updatable varibles, i.e., a state monad)• newIORef :: a -> IO (IORef a)• readIORef :: IORef a -> IO a• writeIORef :: IORef a -> a -> IO ()