More on functional programming Emphasis on Haskell (as a pure functional language) Input and output in Haskell Wrappers / contexts Monads chaining wrapped computations Maybe and lists as monads Return of I/O as a monad Error handling with Either monad
I/O in Haskell as actions Haskell is a pure functional language: functions cannot have side effects So, functions cannot do I/O! But functions can return actions, which can have side effects when they are run For functions, actions are just values (you cannot run them in a function) Bigger actions can be built by chaining actions together
main = do s <- getline f s f x = putstrln ( Hello ++ x ) The Tale of Two Worlds
I/O in Haskell as actions When a Haskell program is run function main is called main returns an action the action is executed the action may evaluate other functions (which return values but have no side effects) the functions/actions may return further actions Bingo: functions are still functional, actions can do I/O
Haskell I/O actions Haskell has functions (and actions) returning basic I/O actions getline :: IO String returns an action from which a string can be extracted putstrln :: String -> IO ( ) takes a string and returns an action which prints the string + line break, no value can be extracted out and more (read from file, network, GUI, )
Chaining actions (do-block) do-notation builds new actions by chaining actions together in sequence Values can be extracted from actions to new variables greet str = do putstrln ( Greetings, ++ str ) main = do putstrln Give your name name <- getline greet name
Chaining actions (do-block) Return clause can be used in actions to produce value that can be extracted askint :: String -> IO Int askint msg = do putstrln msg str <- getline return ( read str ) read :: (Read a) => String -> a show :: (Show a) => a -> String main = do num <- askint Give a number let twice = 2 * num in putstrln ( ( show num ) ++ twice is ++ ( show twice ) )
Return of Maybe Short reminder: Values of type Maybe Int either contain an integer or nothing Just 5: a maybe value that contains 5 Nothing: a maybe value that contains nothing A maybe value is not an integer, but it (maybe) contains / wraps an integer
Using Maybe types Maybe Int is good for error handling (Nothing = error): mysqrt x x >= 0 = Just ( sqrt x ) otherwise = Nothing How to mix Int and Maybe Int? mysqrt ( mysqrt 4 ) -- Error! What we need are monads
Wrappers / contexts A value (of type X) is wrapped inside a value of another type vector<int>, struct, or double* in C++ Maybe in Haskell (Nothing or Just 5) Lists in Haskell ( [ ], [ 3, 5 ], [ 10.. ] ) Lots of others as well
Monads A monad has an operation which takes a wrapped value & a function unwraps the value gives it to the function returns the (wrapped) return value of the function The operation is written >>= (and called bind) (Monads have other operations as well)
Maybe & lists as monads Maybe is defined as a monad! Just 4 >>= mysqrt -- Just 2.0 Just (-1) >>= mysqrt -- Nothing Nothing >>= mysqrt -- Nothing Lists as monads, >>= feeds elements to function and concatenates resulting lists mysqrts x x >= 0 = [ sqrt x, -sqrt x ] otherwise = [ ] [ 4, -1, 9 ] >>= mysqrt -- [ 2, -2, 3, -3 ]
Monads and I/O I/O actions from which a value can be extracted (IO Int, IO String, ) are wrappers They are also monads The I/O do-blocks are exactly the same as the Maybe / list do-blocks
Chaining operations with monads Combining two square root calls mysqsq x = x >>= mysqrt >>= mysqrt What if we want: The do-notation is just syntactic sugar for monad operations! -- Ok foo x = 2 * ( mysqrt x ) + 1 -- Error! foo x = x >>= mysqrt >>= ( \a -> Just ( 2 * a + 1 ) ) foo2 x = do a <- mysqrt x return ( 2 * a + 1 ) foo2 4 -- Just 5.0 foo2 (-2) -- Nothing
Even better errors: Either Either types are like unions in C++, they contain either value of one type or of another Right creates a normal value, Left creates an error value mysqrte :: Float -> Either String Float mysqrte x x >= 0 = Right ( sqrt x ) otherwise = Left sqrt < 0 Either is a monad, foo works! foo3 4 -- Right 5 foo3 (-2) -- Left sqrt < 0 foo3 x = do a <- mysqrte x return ( 2 * a + 1 )