Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Effectful Computations - University of Portsmou...

Avatar for Tom Savage Tom Savage
February 12, 2013

Effectful Computations - University of Portsmouth Computing Society

Avatar for Tom Savage

Tom Savage

February 12, 2013

Other Decks in Programming

Transcript

  1. Outline Introduction to Haskell Some Syntax Lists Data Types Computations

    with Side Effects Functional Purity Random Numbers Effectful Computations Monadic IO
  2. Haskell Purely Functional programming language Functions in the mathematical sense

    Referentially transparent functions Immutable values Controlled side effects Strong, static type system Non-strict (lazy) semantics Extremely expressive syntax which encourages use of abstractions Syntax add :: Int -> Int -> Int add a b = a + b ghci> add 5 2 7 :: Int
  3. Factorial 5! = 1 × 2 × 3 × 4

    × 5 = 120 0! = 1 by definition n! = (n − 1)! × n factorial :: Integer -> Integer factorial 0 = 1 factorial n = factorial (n-1) * n factorial n = product [1..n]
  4. Lists (Parametrically) polymorphic type Either empty, or an element AND

    another list A convenient structure for recursive functions data [a] = a : [a] | [] data List a = Cons a [a] | Null ghci> [1,2,3] == [1..3] == 1 : 2 : 3 : [] True :: Bool
  5. Mapping over lists No loop constructs. map :: (a ->

    b) -> [a] -> [b] map _ [] = [] map f (x:xs) = f x : map f xs ghci> map (*2) [1,2,3] [2,4,6] :: [Int] ghci> map (\x -> if even x then 0 else x) [1,2,3] [1,0,3] :: [Int]
  6. Folding over lists Again, no loop constructs. foldr :: (a

    -> b -> b) -> b -> [a] -> b foldr _ [] s = s foldr f (x:xs) s = f x : foldr f xs s ghci> foldr (*) [1,2,3] 1 6 :: Int => (1 * (2 * (3 * (1))))
  7. Higher-order Functions Just as integers, strings, etc. are values, so

    too are functions Functions can be passed to other functions as arguments Or returned from function calls apply :: (a -> b) -> a -> b apply f x = f x ghci> apply factorial 5 120 :: Integer
  8. Partial Application If you don’t apply all arguments to a

    function, you get another function accepting the remaining the arguments. add :: Int -> Int -> Int add a b = a + b ghci> :type add 5 add 5 :: Int -> Int addFive = add 5 ghci> addFive 2 7 :: Int
  9. Data Types Here are some standard Haskell types: Bool data

    Bool = True | False List data [a] = a : [a] | [] data List a = Cons a [a] | Null Maybe data Maybe a = Just a | Nothing
  10. Functional Purity Easy to reason about program behavior Functionality is

    composable Referentially transparent Sort of useless? How do we do I/O?
  11. Random Numbers Random number generators are impure - their return

    value is a side effect. Python >>> import random >>> random.random() 0.5496590870745269 >>> random.random() 0.6783246403250017
  12. Random Numbers Haskell random :: StdGen -> (a, StdGen) ghci>

    random gen :: (Int, StdGen) (-1113545701,970416508 1655838864) ghci> random gen :: (Int, StdGen) (-1113545701,970416508 1655838864)
  13. Random Numbers random :: StdGen -> (a, StdGen) ghci> let

    gen1 = mkStdGen 123 ghci> let (x, gen2) = random gen1 :: (Int, StdGen) ghci> let (y, gen3) = random gen2 :: (Int, StdGen) ghci> (x, y) (-1113545701,-1952847176)
  14. A Simple Computation Lets take a look at Maybe again:

    data Maybe a = Just a | Nothing Maybe is a useful type for representing some kind of failure (like nullable types in imperative languages). How can we use Maybe effectively?
  15. The Null Check Problem Say we have some functions of

    the general type a -> Maybe b, and want to apply them to a value: head [] = Nothing head (x:_) = Just x halfSqrt x = if x < 0 then Nothing else Just (sqrt x / 2) reciprocal 0 = Nothing reciprocal x = Just (1/x) We have isJust and fromJust from Data.Maybe, but we can have more readable code using case statements and pattern matching.
  16. The Null Check Problem oneOverHalfSqrtHead lst = case head lst

    of Nothing -> Nothing Just x -> case halfSqrt x of Nothing -> Nothing Just x -> case reciprocal x of Nothing -> Nothing Just x -> Just x Remember NullPointerExceptions in Java? This is a common idiom in such languages. Haskell can do better.
  17. What We Want A function which takes a Maybe a

    and a function a -> Maybe b, and returns a Maybe b. joinMaybes :: Maybe a -> (a -> Maybe b) -> Maybe b joinMaybes Nothing _ = Nothing joinMaybes (Just x) f = f x oneOverHalfSqrtHead lst = head lst ‘joinMaybes‘ halfSqrt ‘joinMaybes‘ reciprocal This is a monadic bind function.
  18. Monads Monad is a type class which generalises this functionality.

    Type classes can be compared to interfaces in Java and C#. class Monad m where (>>=) :: m a -> (a -> m b) -> m b (>>) :: m a -> m b -> m b return :: a -> m a fail :: String -> m a instance Monad Maybe where x >>= f = joinMaybes x f x >> y = y return x = Just x We can ignore fail.
  19. Monads We can now use >>= instead of joinMaybes. oneOverHalfSqrtHead

    lst = head lst >>= halfSqrt >>= reciprocal But, other than syntax shared with other monads, what did this give us?
  20. Do Notation Do notation is syntactic sugar for repeated use

    of >>= and >>. oneOverHalfSqrtHead lst = do x <- head lst y <- halfSqrt x reciprocal y Second years will recognise this from their lectures.
  21. Stochastic Computations It’s inconvenient to explicitly thread changing state through

    computations. It’s also error-prone. random :: StdGen -> (a, StdGen) ghci> let gen1 = mkStdGen 123 ghci> let (x, gen2) = random gen1 :: (Int, StdGen) ghci> let (y, gen3) = random gen2 :: (Int, StdGen) ghci> (x, y) (-1113545701,-1952847176) :: (Int, Int)
  22. Monad Random comp :: Rand StdGen (Int, Int) comp =

    do x <- getRandom y <- getRandom return (x, y) ghci> evalRand comp (mkStdGen 123) (-1113545701,-1952847176) :: (Int, Int) evalRand :: RandomGen g => Rand g a -> g -> a
  23. Monadic I/O I/O in Haskell is very similar to computations

    in Rand Instead of a random generator, we’re passing the real world newtype IO a = IO (State# RealWorld -> (#State# RealWorld, a#)) getLine :: IO String putStr :: String -> IO ()
  24. Monadic I/O main :: IO () main = do putStrLn

    "What is your name?" name <- getLine putStrLn ("Hello, "++name++"!") main = putStrLn "What is your name?" >> getLine >>= (\name -> putStrLn ("Hello, "++name++"!"))
  25. Control.Monad The main monad module in base provides a number

    of useful functions. putStrLn :: String -> IO () ghci> :type map putStrLn ["Alice", "Bob", "Eve"] [IO ()] 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 ()
  26. Control.Monad replicate :: Int -> a -> [a] replicateM ::

    Monad m => Int -> m a -> m [a] replicateM_ :: Monad m => Int -> m a -> m () ghci> x <- replicateM 3 (putStr "> " >> getLine) > Foo > Bar > Baz ghci> x ["Foo", "Bar", "Baz"] :: [String]