Haskell a lazy, purely funconal language

COS 326 David Walker Princeton University

slides copyright 2013-2015 David Walker and Andrew W. Appel permission granted to reuse these slides for non-commercial educaonal purposes

Haskell Another cool, typed, funconal • Like OCaml in that: – it is a funconal language with parametric polymorphism • Unlike OCaml in that it is: – pure: funcons with type a -> b have no effect! – lazy: f e does not evaluate e right away; passes e to f – has a weak module system; uses type classes – has a very sophiscated type system with higher kinds – has several cool extensions for concurrent and parallel programming including transaconal memory Glasgow in the 1990s ghc stands for “Glasgow Haskell Compiler”

Phil Wadler Simon Peyton Jones (now at Microso Research, (now at U. Edinburgh) Cambridge, England) Creators of Haskell language

Edinburgh in the 1970s

Robin Milner, Luca Cardelli, Luis Damas, Mads Toe . . . created ML language

Why are we geng all our funconal programming languages from Scotland? HASKELL BASICS Haskell Definions • Mostly like OCaml, with a few small syntacc differences – parameters are immutable by default – let declaraons introduce new funcons and value

foo z = let triple x = x*3 in triple z

– equivalently, we might use a "where" definion:

foo z = triple z where triple x = x*3

Haskell Indentaon • Haskell, like Python, but unlike Java, OCaml or math wrien in a notebook, has semancally meaningful indentaon • Wrong:

must indent copies k n = funcon if n == 0 then [] body else k : copies k (n-1)

zap z = zap z = let x = z let x = z indent z + z y = y = z + z z + z in x + y in x + y indent y = ... Haskell Indentaon • Haskell, like Python, but unlike Java, OCaml or math wrien in a notebook, has semancally meaningful indentaon • Right: beginning of x defines indentaon level

copies k n = zap z = zap z = if n == 0 then let let x = z [] x = z y = z + z else y = z + z in k : copies k (n-1) in x + y x + y

Haskell Types • We have the opon of declaring the type of a definion prior to the definion itself just to be annoying, Haskell uses "::" for "has type" and uses ":" for "cons" – the opposite zap :: Int -> Int of ML zap z = let x = z y = z + z in x + y

Tuples • Haskell uses tuples like ML – constructed by enclosing a sequence of values in parens:

(‘b’, 4) :: (Char, Integer)

– deconstructed (used) via paern matching:

easytoo :: (Integer, Integer, Integer) -> Integer easytoo (x, y, z) = x + y * z Lists • Lists are very similar to ML lists but the type is wrien [t] instead of "t list"

[1, 2, 3] :: [Integer]

[‘a’, ‘b’, ‘c’] :: [Char]

• [ ] is the empty list (called nil) • 1:2:[] is a list with 2 elements • String is a synonym for [Char] • We can build lists of lists:

[ [1, 2], [3], [8, 9, 10] ] :: [ [ Integer ] ]

Funcons over lists

-- Sum the elements of a list

listSum :: [ Integer ] -> Integer

listSum [ ] = 0 listSum (x:xs) = x + listSum xs

common to write funcons using mulple clauses, where each clause matches on a different case Funcons deconstrucng lists

-- Sum the elements of a list

listSum :: [ Integer ] -> Integer

listSum [ ] = 0 listSum (x:xs) = x + listSum xs lower case leer = any type at all (a type variable) upper case leer = a concrete type length :: [a] -> Int

length [ ] = 0 length (x:xs) = 1 + length xs Funcons deconstrucng lists

-- Sum the elements of a list

listSum :: [ Integer ] -> Integer

listSum [ ] = 0 listSum (x:xs) = x + listSum xs

cat :: [a] -> [a] -> [a] length :: [a] -> Int cat [ ] xs2 = xs2

cat (x:xs) xs2 = x:(cat xs xs2) length [ ] = 0 length (x:xs) = 1 + length xs Funcons deconstrucng lists

-- Sum the elements of a list

listSum :: [ Integer ] -> Integer

listSum [ ] = 0 listSum (x:xs) = x + listSum xs

cat :: [a] -> [a] -> [a] length :: [a] -> Int cat [ ] xs2 = xs2

cat (x:xs) xs2 = x:(cat xs xs2) length [ ] = 0 length (x:xs) = 1 + length xs (++) :: [a] -> [a] -> [a]

(++) [ ] xs2 = xs2 (++) (x:xs) xs2 = x:(xs ++ xs2) PURITY & SUBSTITUTION OF EQUALS FOR EQUALS Substuon of Equals for Equals • A key law about Haskell programs:

let x = in ... ... ...... x ... x ... =

• For example:

let x = 4 `div` 2 in (4 `div` 2) + 5 + (4 `div` 2) x + 5 + x =

= 9

*Note: not necessarily the same run me; (4 `div` 2) will be evaluated twice instead of once. Substuon of Equals for Equals • We'd also like to use funconal abstracon without penalty halve :: Int -> Int halve n = n `div` 2 • And instead of telling clients about all implementaon details, simply expose key laws:

Lemma 1: for all n, if even n then (halve n + halve n) = n

• Now we can reason locally within the client: let x = halve 4 in x + y + x = (halve 4) + y + (halve 4) (substuon) = (halve 4) + (halve 4) + y (arithmec) = 4 + y (Lemma 1)

Computaonal Effects • What happens when we add mutable data structures? • Consider this OCaml program: let x = ref 0 foo : int -> int

let foo (y:int) : int = x := !x + 1; arg + !x;

• We lose a lot of reasoning power!

let y = foo 3 in foo 3 + foo 3 y + y ≠ Computaonal Effects • What happens when we add mutable data structures? • Consider this OCaml program: let x = ref 0 foo : int -> int

let foo (y:int) : int = x := !x + 1; arg + !x;

• We lose a lot of reasoning power!

let y = foo 3 in foo 3 + foo 3 y + y ≠

8 9 Computaonal Effects • What happens when we add mutable data structures? • Consider this OCaml program: foo : int -> int

let foo (y:int) : int = print_int y; arg + !x;

• We lose a lot of reasoning power!

let y = foo 3 in foo 3 + foo 3 y + y ≠

6 prinng "3" 6 prinng "33" Computaonal Effects • A funcon has an effect if its behavior cannot be specified exclusively as a relaon between its input and its output – I/O is an effect – An imperave update of a data structure is an effect • When funcons can no longer be described exclusively in terms of the relaonship between arguments and results – many, many fewer equaonal laws hold – In general, in OCaml,

let x = in ... x ... x ... ≠ ... ... ...

– In general, in Haskell, let x = in ... x ... x ... = ... ... ... Computaonal Effects • A funcon has an effect if its behavior cannot be specified exclusively as a relaon between its input and its output – I/O is an effect – An imperave update of a data structure is an effect • When funcons can no longer be described exclusively in This is kind of magical! terms of the relaonship between arguments and results Haskell has substuon of – many, many fewer equals for equals equaonal laws hold – In general, in AND ALSO HAS EFFECTS. OCaml, But how? let x = in ... x ... x ... ≠ ... ... ...

– In general, in Haskell, let x = in ... x ... x ... = ... ... ... DIGRESSION: MONADS AND LIST COMPREHENSIONS List comprehensions Example: Find all (a,b,c) such that 1 ≤ a ≤ b ≤ c ≤ 25 and a2+b2=c2

p a b c = a*a + b*b = c*c triples = do a <- [1..25] b <- [a..25] c <- [b..25] guard (p a b c) return (a,b,c)

Source: Greg Bacon, hp://gbacon.blogspot.com/2009/07/programmable-semicolon-explained.html

List comprehensions Example: Find all (a,b,c) such that 1 ≤ a ≤ b ≤ c ≤ 25 and a2+b2=c2

p a b c = a*a + b*b = c*c

triples = do It looks like some sort of “iterator” feature; a <- [1..25] but Haskell doesn’t have built-in “iterators.” b <- [a..25] It's got something more general! Iterators arise as a natural consequence of: c <- [b..25] pure funconal programming guard (p a b c) + lazy evaluaon + lists return (a,b,c) + a nice notaon for monadic computaons

Now, let’s “unpack” this to see how that works.

Source: Greg Bacon, hp://gbacon.blogspot.com/2009/07/ programmable-semicolon-explained.html

List comprehensions Example: Find all (a,b,c) such that 1 ≤ a ≤ b ≤ c ≤ 25 and a2+b2=c2

triples = do { a <- [1..25] ;

b <- [a..25] ; An alternate syntax using semi-colons. c <- [b..25] ; The semi-colon operaon guard (p a b c) ; actually does a lot of work. It does more than just return (a,b,c) "move on to the next statement". } It "composes" the funcons defined by each statement.

Source: Greg Bacon, hp://gbacon.blogspot.com/2009/07/ programmable-semicolon-explained.html

List comprehensions Example: Find all (a,b,c) such that 1 ≤ a ≤ b ≤ c ≤ 25 and a2+b2=c2 triples = do { a <- [1..25] ;

b <- [a..25] ; This is just the “range” operator for lists; c <- [b..25] ; [1..5] is just [1,2,3,4,5]. Easy to construct guard (p a b c) ; this with “let rec” in ML, or as a recursive funcon in Haskell. No magic here. return (a,b,c) }

List comprehensions “a <- e ; …” is just a notaonal abbreviaon for the monadic bind operator that you’ve seen before. Haskell makes it easy to introduce notaonal abbreviaons That’s λ from lambda-calculus; in ML you’d write “fun” triples = do { triples = a <- [1..25] ; [1..25] >>= \a -> b <- [a..25] ; [a..25] >>= \b -> c <- [b..25] ; [b..25] >>= \c -> guard (p a b c) >> guard (p a b c) ; return (a,b,c) return (a,b,c) }

List comprehensions “a <- e ; …” is just a notaonal abbreviaon for the monadic bind operator that you’ve seen before. Haskell makes it easy to introduce notaonal abbreviaons That’s λ from lambda-calculus; in ML you’d write “fun” triples = do { triples = a <- [1..25] ; [1..25] >>= \a -> b <- [a..25] ; [a..25] >>= \b -> c <- [b..25] ; [b..25] >>= \c -> guard (p a b c) >> guard (p a b c) ; return (a,b,c) return (a,b,c) }

do {a <- e1; e2} == e1 >>= (\a -> e2) == e1 >>= (fun a -> e2) List Monad You’ve seen monads before, but let me remind you:

(* in Ocaml notaon, not Haskell! *) One way to think of a monad 'a M: It is a special sort of "container" for 'a. module type MONAD = sig return x : puts x into the container

type ‘a M c >>= f : extracts items from c; val return : ‘a -> ‘a M then pushes them through f, which generates new container val (>>=) : ‘a M -> (‘a -> ‘b M) -> ‘b M Bind (c >> f) allows you to compute with end the items inside the container pronounced “bind” List Monad You’ve seen monads before, but let me remind you:

(* in Ocaml notaon, not Haskell! *) One way to think of a monad 'a M: It is a special sort of "container" for 'a. module type MONAD = sig return x : puts x into the container

type ‘a M c >>= f : extracts items from c; val return : ‘a -> ‘a M then pushes them through f, which generates new container val (>>=) : ‘a M -> (‘a -> ‘b M) -> ‘b M Bind (c >> f) allows you to compute with end the items inside the container

pronounced “bind” When we did error processing, the container was an opon type: 'a opon. At most one thing was in the container at a me. List Monad Here, the monad we want is the List monad with concatMap

(* in Ocaml notaon, not Haskell! *) module type MONAD = sig

type ‘a M

val return : ‘a -> ‘a M

val (>>=) : ‘a M -> (‘a -> ‘b M) -> ‘b M end

One more thing: >> is just a “bind” that throws away its argument.

let (>>) (m: ‘a M) (f: ‘b M) : ‘b M = m >>= fun _ -> f List Monad Here, the monad we want is the List monad with concatMap

(* in Ocaml notaon, not Haskell! *) (* in Ocaml notaon, not Haskell! *) module type MONAD = sig module ListMonad : MONAD

type ‘a M type ‘a M = ‘a list

val return : ‘a -> ‘a M let return (x: ‘a) = [x]

val (>>=) : ‘a M -> (‘a -> ‘b M) -> ‘b M let (>>=) (as: ‘a list) (f: ‘a -> ‘b list) : ‘b list = reduce append nil (map f as) end end List Monad Here, the monad we want is the List monad with concatMap

(* in Ocaml notaon, not Haskell! *) (* in Ocaml notaon, not Haskell! *) module type MONAD = sig module ListMonad : MONAD

type ‘a M type ‘a M = ‘a list

val return : ‘a -> ‘a M let return (x: ‘a) = [x]

val (>>=) : ‘a M -> (‘a -> ‘b M) -> ‘b M let (>>=) (as: ‘a list) (f: ‘a -> ‘b list) : ‘b list = reduce append nil (map f as) end end

let double a = [a;a]

[1;2;3] >>= double == reduce append nil [ [1;1]; [2;2]; [3;3] ] == [1;1;2;2;3;3] Example

[1,2,3] >>= \a -> let f a = return (a,a) in return (a,a) = reduce append nil (map f [1,2,3])

return x = [x]

(>>=) xs f = reduce append nil (map f xs) Example

[1,2,3] >>= \a -> let f a = return (a,a) in return (a,a) = reduce append nil (map f [1,2,3])

let f a = return (a,a) = in reduce append nil [f 1, f 2, f 3]

return x = [x]

(>>=) xs f = reduce append nil (map f xs) Example

[1,2,3] >>= \a -> let f a = return (a,a) in return (a,a) = reduce append nil (map f [1,2,3])

let f a = return (a,a) = in reduce append nil [f 1, f 2, f 3]

reduce append nil [return (1,1), = return (2,2), return (3,3) ]

return x = [x]

(>>=) xs f = reduce append nil (map f xs) Example

[1,2,3] >>= \a -> let f a = return (a,a) in return (a,a) = reduce append nil (map f [1,2,3])

let f a = return (a,a) = in reduce append nil [f 1, f 2, f 3]

reduce append nil [return (1,1), = return (2,2), return (3,3) ]

reduce append nil [ [(1,1)], = [(2,2)], [(3,3)] ] return x = [x]

(>>=) xs f = reduce append nil (map f xs) Example

[1,2,3] >>= \a -> let f a = return (a,a) in return (a,a) = reduce append nil (map f [1,2,3])

let f a = return (a,a) = in reduce append nil [f 1, f 2, f 3]

reduce append nil [return (1,1), = return (2,2), return (3,3) ]

reduce append nil [ [(1,1)], = [(2,2)], [(3,3)] ] return x = [x] [ (1,1), (2,2), (3,3) ] = (>>=) xs f = reduce append nil (map f xs) Example foo = [1..3] >>= \a -> let f a = [a..4] >>= \b -> return (a,b) [a..4] >>= \b -> = in reduce app nil (map f [1,2,3]) return (a,b)

= let f a = [a..4] >>= \b -> return (a,b) in reduce app nil [f 1, f 2, f 3]

reduce app nil = [[1,2,3,4] >>= \b -> return (1,b), [2,3,4] >>= \b -> return (2,b), [3,4] >>= \b -> return (3,b) ] return (x: ‘a) = [x]

(>>=) (al: [a]) (f: a -> [b]) : [b] = reduce app nil (map f al) Example foo = [1..3] >>= \a -> let f a = [a..4] >>= \b -> return (a,b) [a..4] >>= \b -> = in reduce app nil (map f [1,2,3]) return (a,b)

= let f a = [a..4] >>= \b -> return (a,b) in reduce app nil [f 1, f 2, f 3]

reduce app nil = [[1,2,3,4] >>= \b -> return (1,b), [2,3,4] >>= \b -> return (2,b), [3,4] >>= \b -> return (3,b) ] return (x: ‘a) = [x]

(>>=) (al: [a]) (f: a -> [b]) : [b] = reduce app nil (map f al) Example reduce app nil foo = [[1,2,3,4] >>= \b -> return (1,b), [1..3] >>= \a -> [2,3,4] >>= \b -> return (2,b), [a..4] >>= \b -> = [3,4] >>= \b -> return (3,b) return (a,b) ]

reduce app nil [reduce app nil [return(1,1),return(1,2),return(1,3),return(1,4)], = reduce app nil [return(2,2),return(2,3),return(2,4)], reduce app nil [return(3,3),return(3,4)] ] reduce app nil [reduce app nil [[(1,1)],[(1,2)],[(1,3)],[(1,4)]], reduce app nil [[(2,2)],[(2,3)],[(2,4)]], = reduce app nil [[(3,3)],[(3,4)]] ] return (x: ‘a) = [x]

(>>=) (al: [a]) (f: a -> [b]) : [b] = reduce app nil (map f al) Example reduce app nil [1..3] >>= \a -> [reduce app nil [[(1,1)],[(1,2)],[(1,3)],[(1,4)]], [a..4] >>= \b -> = reduce app nil [[(2,2)],[(2,3)],[(2,4)]], return (a,b) reduce app nil [[(3,3)],[(3,4)]] ]

reduce app nil [[(1,1),(1,2),(1,3),(1,4)], = [(2,2),(2,3),(2,4)]], [(3,3),(3,4)]] ]

= [(1,1),(1,2),(1,3),(1,4),(2,2),(2,3),(2,4),(3,3),(3,4)] return (x: ‘a) = [x]

(>>=) (al: [a]) (f: a -> [b]) : [b] = reduce app nil (map f al) Is it really an iterator?

do a <- [1..3] [(1,1),(1,2),(1,3),(1,4),(2,2),(2,3),(2,4),(3,3),(3,4)] b <- [a..4] = return (a,b)

It doesn’t seem like “in the spirit of an iterator” to return the whole list; it should return the pairs (1,1); (1,2); … one at a me, on demand. That’s what we mean by an iterator.

Aha! Every expression in Haskell is lazy. Everything is on-demand. This really is an iterator! Returning to the “Pythagorean triples” … do [1..25] >>= \a -> [1..25] >>= \a -> a <- [1..25] [a..25] >>= \b -> [a..25] >>= \b -> b <- [a..25] [b..25] >>= \c -> [b..25] >>= \c -> c <- [b..25] guard (p a b c) >> guard (p a b c) >>= \ _ -> guard (p a b c) return (a,b,c) return (a,b,c) return (a,b,c)

guard :: (MonadPlus m) => Bool -> m () guard True = return () guard False = mzero “return ()” in the List monad is [ () ]

The “zero” for the List monad is [ ] Example of “guard” do [1..5] >>= \a -> a <- [1..5] = guard (isprime a) >>= \ _ -> guard (isprime a) return (a+1) return (a+1)

f a = guard (isprime a) >>= \ _ -> return (a+1) = reduce app nil (map f [1,2,3,4,5])

reduce app nil [ = guard (isprime 1) >> \_ return (1+1), guard (isprime 2) >> \_ return (2+1), guard (isprime 3) >> \_ return (3+1), guard True = [ () ] guard (isprime 4) >> \_ return (4+1), guard False = [ ] guard (isprime 5) >> \_ return (5+1) ] Example of “guard” do reduce app nil [ a <- [1..5] guard False >> \_ return (1+1), guard (isprime a) = guard True >> \_ return (2+1), return (a+1) guard True >> \_ return (3+1), guard False >> \_ return (4+1), guard True >> \_ return (5+1) ] reduce app nil [ fold app nil (map (\_ return (1+1)) []), fold app nil (map (\_ return (2+1)) [ () ]), guard True = [ () ] = fold app nil (map (\_ return (3+1)) [ () ]), guard False = [ ] fold app nil (map (\_ return (4+1)) []), fold app nil (map (\_ return (5+1)) [ () ]) ] = reduce app nil [ [], [2+1], [3+1], [] [5+1] ] = [2+1, 3+1, 5+1] = [2+1, 3+1, 5+1] = [3,4,6] Returning to the “Pythagorean triples” … do a <- [1..25] b <- [a..25] This is called a “list comprehension” c <- [b..25] guard (p a b c) return (a,b,c) In monadic computaon, in any collecon monad (not just List), guard serves as a filter for comprehensions

There was no magic here (except for lazy funconal programming). Monads, “bind”, “guard”, the List monad, are all defined as user-level funcons in the Haskell language itself. HASKELL EFFECTS INPUT AND OUTPUT I/O in Haskell • Haskell has a special kind of value called an acon that describes an effect on the world

• Pure acons, which just do something and have no interesng result are values of type IO ()

• Eg: putStr takes a string and yields an acon describing the act of displaying this string on stdout

-- writes string to stdout putStr :: String -> IO ()

-- writes string to stdout followed by newline putStrLn :: String -> IO () I/O in Haskell • When do acons actually happen?

• Acons happen under two circumstances:* 1. the acon defined by main happens when your program is executed (ie: when ghc compiles your program and then you run it)

2. the acon defined by any expression happens when that expression is wrien at the ghci interpreter prompt (this is prey similar to the last one – the expression is essenal an enre program)

* there is one other circumstance: Haskell contains some special, unsafe funcons that will perform I/O, most notably System.IO.Unsafe.unsafePerformIO so I lied when earlier when I said the equaon holds in general for Haskell programs ... I/O in Haskell hello.hs: main :: IO () main = putStrLn “Hello world”

in my shell: $ ghc hello.hs [1 of 1] Compiling Main ( hello.hs, hello.o ) Linking hello.exe ...

$ ./hello.exe hello world!

bar.hs: bar :: Int -> IO () bar n = putStrLn (show n ++ “ is a super number”)

main :: IO () main = bar 6

in my shell: $ ghcii.sh GHCi, version 7.0.3: hp://www.haskell.org/ghc/ :? for help Loading package ghc-prim ... linking ... done. Loading package integer-gmp ... linking ... done. Loading package base ... linking ... done. Loading package ffi-1.0 ... linking ... done. Prelude> :l bar [1 of 1] Compiling Main ( bar.hs, interpreted ) Ok, modules loaded: Main. *Main> bar 17 17 is a super number *Main> main 6 is a super number *Main> Acons • Acons are descripons of effects on the world. Simply wring an acon does not, by itself cause anything to happen

bar.hs: hellos :: [IO ()] hellos = [putStrLn “Hi”, putStrLn “Hey”, putStrLn “Top of the morning to you”]

main = hellos !! 2

� !! � in my shell: Prelude> :l hellos gives �th element ... of list � *Main> main Top of the morning to you *Main> Acons • Acons are just like any other value -- we can store them, pass them to funcons, rearrange them, etc: sequence_ :: [IO ()] -> IO ()

baz.hs: hellos :: [IO ()] hellos = [putStrLn “Hi”, putStrLn “Hey”, putStrLn “Top of the morning to you”]

main = sequence_ (reverse hellos)

in my shell: Prelude> :l hellos ... *Main> main Top of the morning to you Hey HI Combining Acons • The infix operator >> takes two acons a and b and yields an acon that describes the effect of execung a then execung b aerward

howdy :: IO () howdy = putStr “how” >> putStrLn “dy”

• To combine many acons, use do notaon:

bonjour :: IO () bonjour = do putStr “Bonjour!” putStr “ ” putStrLn “Comment ca va?” Quick Aside: Back to SEQEQ* • Do we sll have it? Yes!

let a = PutStrLn "hello" in do do a = PutStrLn "hello" a PutStrLn "hello"

an acon made up of doing a an acon made up of doing and then doing a again where the PutStrLn "hello" acon and a is the PutStrLn "hello" acon then doing the PutStrLn "hello" acon again

* SEQEQ = substuon of equals for equals Input Acons • Some acons have an effect and yield a result:

-- get a line of input getLine :: IO String

-- get all of standard input unl end-of-file encountered getContents :: IO String

-- get command line argument list getArgs :: IO [String]

• What can we do with these kinds of acons? – we can extract the value and sequence the effect with another: Input Acons • Some acons have an effect and yield a result:

-- get a line of input getLine :: IO String

-- get all of standard input unl end-of-file encountered getContents :: IO String

-- get command line argument list getArgs :: IO [String]

• What can we do with these kinds of acons? – we can extract the value and sequence the effect with another:

do s <- getLine putStrLn s Input Acons • Some acons have an effect and yield a result:

-- get a line of input getLine :: IO String

-- get all of standard input unl end-of-file encountered getContents :: IO String

-- get command line argument list getArgs :: IO [String]

• What can we do with these kinds of acons? – we can extract the value and sequence the effect with another:

do s <- getLine putStrLn s s has type string getLine has type IO string Input Acons • Some acons have an effect and yield a result:

-- get a line of input getLine :: IO String

-- get all of standard input unl end-of-file encountered Think of type IO String getContents :: IO String as a box containing a computaon that will -- get command line argument list do some work and then The "do" packages up the getArgs :: IO [String] produce a string. getLine getLine and the putStrLn is such a box. the <- gets s acons up in to a bigger • s the String out of the box composite box What can we do with these kinds of acons? – we can extract the value and sequence the effect with another:

do s <- getLine putStrLn s Input Acons • A whole program:

main :: IO () main = do putStrLn “What’s your name?” s <- getLine putStr “Hey, “ putStr s putStrLn “, cool name!” contains readFile import System.IO import modules import System.Environment contains getArgs, getProgName processArgs :: [String] -> String processArgs [a] = a processArgs _ = ""

echo :: String -> IO () echo "" = putStrLn "Bad Args!" echo fileName = do s <- readFile fileName putStrLn "Here it is:" putStrLn "***********" putStr s putStrLn "\n***********" <- notaon:

main :: IO () RHS has type IO T main = do LHS has type T args <- getArgs let fileName = processArgs args let notaon: echo fileName RHS has type T LHS has type T SEQEQ (Again!) • Recall: s1 ++ s2 concatenates String s1 with String s2 • A valid reasoning step:

let s = "hello" in do do putStrLn (s ++ s) putStrLn ("hello" ++ "hello") =

SEQEQ (Again!) • Recall: s1 ++ s2 concatenates String s1 with String s2 • A valid reasoning step:

let s = "hello" in do do putStrLn (s ++ s) putStrLn ("hello" ++ "hello") = • A valid reasoning step:

do let s = "hello" do putStrLn (s ++ s) = putStrLn ("hello" ++ "hello")

SEQEQ (Again!) • Recall: s1 ++ s2 concatenates String s1 with String s2 • A valid reasoning step:

let s = "hello" in do do putStrLn (s ++ s) putStrLn ("hello" ++ "hello") = • A valid reasoning step:

do let s = "hello" do putStrLn (s ++ s) = putStrLn ("hello" ++ "hello") • Wait, what about this: wrong type: getLine :: IO String do s <- getLine do putStrLn (s ++ s) ≠ putStrLn (getLine ++ getLine) SEQEQ (Again!) • Invalid reasoning step?

let s = getLine in ? do do putStrLn (s ++ s) = putStrLn (getLine ++ getLine) SEQEQ (Again!) • Invalid reasoning step?

let s = getLine in ? do do putStrLn (s ++ s) = putStrLn (getLine ++ getLine)

wrong type: wrong type: s :: IO String getLine :: IO String SEQEQ (Again!) • Invalid reasoning step?

let s = getLine in ? do do putStrLn (s ++ s) = putStrLn (getLine ++ getLine)

wrong type: wrong type: s :: IO String getLine :: IO String

• The Haskell type system shows x <- e is different from let x = e – x has a different type in each case – let x = e enables substuon of e for x in what follows – x <- e does not enable substuon -- aempng substuon leaves you with code that won't even type check because x and e have different types (type T vs. type IO T) Magic? The List monad was not “magic.” List comprehensions, “iterators”, guards, could all be expressed in the pure funconal language with no magic extensions.

The IO monad is magic. But all the monadic combinators for manipulang it, sequencing, etc., are not magic; they are expressible in the pure funconal language. HASKELL EFFECTS: REFERENCES Coming Back to References Again • Remember this OCaml funcon: let x = ref 0 foo : int -> int

let foo (y:int) : int = x := !x + 1; arg + !x; • We noced that: let y = foo 3 in foo 3 + foo 3 : int y + y ≠ • What if we write something similar in Haskell? Coming Back to References Again • Remember this OCaml funcon: let x = ref 0 foo : int -> int

let foo (y:int) : int = x := !x + 1; arg + !x; • We noced that: let y = foo 3 in foo 3 + foo 3 : int y + y ≠ • What if we write something similar in Haskell? x :: Ref int doesn't type check

foo :: int -> int foo y = x := read x + 1; arg + !x; Haskell Types Tell You Where the Effects Aren't!

Haskell funcon types are pure -- totally effect-free

foo :: int -> int

Haskell’s type system forces* purity on funcons with type a -> b • no prinng • no mutable data • no reading from files • no concurrency • no benign effects (like memoizaon)

* except for unsafePerformIO exp :: int Same with expressions. Expressions with just type int have no effect! Haskell Types Tell You Where the Effects Aren't!

foo :: int -> int totally pure funcon

suspended (lazy) computaon :: IO int that performs effects when executed Haskell Types Tell You Where the Effects Aren't!

foo :: int -> int totally pure funcon

suspended (lazy) computaon :: IO int that performs effects when executed

bar :: int -> IO int totally pure funcon that returns suspended effecul acon Haskell Types Tell You Where the Effects Aren't!

print :: string -> IO ()

reverse :: string -> string

reverse “hello” :: string

print (reverse “hello”) :: IO ()

the type system always tells you when an effect has happened – effects can’t “escape” the I/O monad References

read :: Ref a -> IO a

(+) :: int -> int -> int r :: Ref int

(read r) + 3 :: int

Doesn’t type check References

read :: Ref a -> IO a

(+) :: int -> int -> int

r :: Ref int

do x <- read r return (x + 3)

creates a composion acon that reads a reference and the "return" acon has no effect; produces the value in that reference it just returns its value plus 3 new :: a -> IO (Ref a) Mutable State read :: Ref a -> IO a write :: Ref a -> a -> IO ()

Haskell uses new, read, and write* funcons within the IO Monad to manage mutable state.

main = do r <- new 0 -- let r = ref 0 in inc r -- r := !r+1; s <- read r -- s := !r; print s

inc :: Ref Int -> IO () inc r = do v <- read r -- v := !r write r (v+1) -- r := !v +1

* actually newRef, readRef, writeRef, … MONADS The Bigger Picture • Haskell's type system (at least the part of it that we have seen so far) is very similar to the ML type system – eg: typing rules for pure primives (funcons, pairs, lists, etc) are similar in both cases: • if f : t1 -> t2 and e : t1 then (f e) : t2 • if e1 : t1 and e2 : t2 then (e1, e2) : (t1, t2)

• What Haskell has done that is special is: – it has been very careful to give effecul primives special types involving "IO t" – allows composion of acons with type "IO t" • the IO data type + its composion funcons are called a monad – it has syntax for programming with monads (do notaon) – ML could do those things too (ie: these decisions do not depend upon a language have lazy evaluaon)

We can talk about what monads are by referring to their interface

Recall an interface declares some new abstract types and some operaons over values with those abstract types. For example:

There are lots of different implementations of such module type CONTAINER = sig containers: queues, stacks, sets, randomized sets, ... type ‘a t (* the type of the container *)

val empty : ‘a t

val insert : ‘a -> ‘a t -> ‘a t

val remove : ‘a t -> ‘a opon * ‘a t

val fold : (‘a -> ‘b -> ‘b) -> ‘b -> ‘a t -> ‘b end

We can talk about what monads are by referring to their interface

Recall an interface declares some new abstract types and some operaons over values with those abstract types. For example:

There are lots of different implementations of such module type CONTAINER = sig containers: queues, stacks, sets, randomized sets, ... type ‘a t (* the type of the container *)

val empty : ‘a t Interfaces can come with some equations one expects every implementation to val insert : ‘a -> ‘a t -> ‘a t satisfy. eg:

val remove : ‘a t -> ‘a opon * ‘a t fold f base empty == base The equations specify some, val fold : (‘a -> ‘b -> ‘b) -> ‘b -> ‘a t -> ‘b but not all of the behavior of the module (eg: stacks and end queues remove elements in different orders)

Monads A monad is just a parcular interface. Two views: – (1) interface for a very generic container, with operaons designed to support composion of computaons over the contents of containers – (2) interface for an abstract computaon that does some “bookkeeping” on the side. By “bookkeeping” we mean “effects”. Once again, the support for composion is key. – since funconal programmers know that funcons are data, the two views actually coincide

Monads A monad is just a parcular interface. Two views: – (1) interface for a very generic container • supports composion of computaons over the contents of containers – (2) interface for an abstract computaon that does some “book keeping.” • bookkeeping is code for “has an effect”. Once again, the support for composion is key. – since funconal programmers know that funcons are data, the two views actually coincide

Many different kinds of monads: – monads for handling/accumulang errors – monads for processing collecons en masse – monads for logging strings that should be printed – monads for coordinang concurrent threads (see OCaml Async library) – monads for backtracking search – monads for transaconal memory

Monads A monad is just a parcular interface. Two views: – (1) interface for a very generic container • supports composion of computaons over the contents of containers – (2) interface for an abstract computaon that does some “book keeping.” • bookkeeping is code for “has an effect”. Once again, the support for composion is key. – since funconal programmers know that funcons are data, the two views actually coincide

Because a monad is just a parcular interface (with many useful implementaons), you can implement monads in any language – But, Haskell is famous for them because it has a special built-in syntax that makes monads parcularly easy and elegant to use – F#, Scala have adopted similar syntacc ideas – Monads also play a very special role in the overall design of the Haskell language due to the confinement of effects • Monads are the right way to enable both effects and substuon of equals for equals

What is the monad interface? module type MONAD = sig

type ‘a M + some equaons specifying val return : ‘a -> ‘a M how return and bind are required to interact val (>>=) : ‘a M -> (‘a -> ‘b M) -> ‘b M end

Consider first the “container interpretaon”: § ‘a M is a container for values with type ‘a § return x puts x in the container § bind c f takes the values in c out of the container and applies f to them, forming a new container holding the results - bind c f is oen wrien as: c >>= f

The Opons as a Container module type MONAD = sig module OponMonad = struct

type ‘a M type ‘a M = ‘a opon

val return : ‘a -> ‘a M let return x = Some x

val (>>=) : ‘a M -> (‘a -> ‘b M) -> ‘b M let (>>=) c f = match c with end None -> None | Some v -> f v

end

put value in a container take value v out of a container c and then apply f, producing a new container The Opons as a Container

module type MONAD = sig module OponMonad = struct

type ‘a M type ‘a M = ‘a opon

val return : ‘a -> ‘a M let return x = Some x

val (>>=) : ‘a M -> (‘a -> ‘b M) -> ‘b M let (>>=) c f = match c with end None -> None | Some v -> f v using the option container: type file_name = string end val read_file : file_name -> string M put value in a container let concat f1 f2 = take value v out readfile f1 >>= (fun contents1 -> of a container c readfile f2 >>= (fun contents2 -> and then apply f, return (contents1 ^ contents2) producing a new container Book-keeping Interpretaon: The Opon Monad as Possibly Erroneous Computaon module type MONAD = sig module ErrorMonad = struct

type ‘a M type ‘a M = ‘a opon

val return : ‘a -> ‘a M let return x = Some x

val (>>=) : ‘a M -> (‘a -> ‘b M) -> ‘b let (>>=) c f = M match c with None -> None end | Some v -> f v using the error monad: type file_name = string end val read_file : file_name -> string M compose bookkeeping: setting up let concat f1 f2 = check to see if bookkeeping readfile f1 >>= (fun contents1 -> error has occurred, for error readfile f2 >>= (fun contents2 -> if so return None, processing return (contents1 ^ contents2) else continue Lists as Containers module type MONAD = sig module ListMonad = struct type ‘a M type ‘a M = ‘a list val return : ‘a -> ‘a M let return x = [x] val (>>=) : ‘a M -> (‘a -> ‘b M) -> ‘b M let (>>=) c f = end List.flaen (List.map f c) using the list monad: end random_sample : unit -> int M monte_carlo : int -> int -> int -> result put element let experiments : result M = apply f to all elements into list random_sample() >>= (fun s1 -> of the list c, creating a container random_sample() >>= (fun s2 -> list of lists and then random_sample() >>= (fun s3 -> flatten results in to return (monte_carlo s1 s2 s3) single list Lists as Containers module type MONAD = sig module ListMonad = struct type ‘a M type ‘a M = ‘a list val return : ‘a -> ‘a M let return x = [x] val (>>=) : ‘a M -> (‘a -> ‘b M) -> ‘b M let (>>=) c f = end List.flaen (List.map f c) using the list monad: end random_sample : unit -> int M monte_carlo : int -> int -> int -> result one result; let experiments : result M = compose many no nondeterminism random_sample() >>= (fun s1 -> possible results (c) random_sample() >>= (fun s2 -> with a nondeterministic random_sample() >>= (fun s3 -> continuation f return (monte_carlo s1 s2 s3) A Container with a String on the Side (aka: A logging/prinng monad) module type MONAD = sig module LoggingMonad = struct

type ‘a M type ‘a M = ‘a * string

val return : ‘a -> ‘a M let return x = (x, “”)

val (>>=) : ‘a M -> (‘a -> ‘b M) -> ‘b M let (>>=) c f = let (v, s) = c in end let (v’,s’) = f v in using the logging monad: (v’, s ^ s’) record : (‘a -> ‘b) -> ‘a -> string -> ‘b M end let record f x s = (f x, s) nothing logged let do x = concatenate the yet record read x “read it” >>= (fun v -> log of c with record write v “wrote it” >>= (fun _ -> the log produced record write v “wrote it again” >>= (fun _ -> by running f return v A Container with a State on the Side (aka: A logging/prinng monad) module type MONAD = sig module StateMonad = struct

type ‘a M type state = address int map

val return : ‘a -> ‘a M type ‘a M = ‘a * (state -> state)

val (>>=) : ‘a M -> (‘a -> ‘b M) -> ‘b M let return x = ??? end let (>>=) c f = ???

let read a = ???

let write a v = ???

end Monad Laws Just like one expects any CONTAINER to behave in a parcular way, one has expectaons of MONADs.

Le identy: “return does nothing observable” (1) return v >>= f == f v

Right identy: “return sll doesn’t do anything observable” (2) m >>= return == m

Associavity: “composing m with f first and then doing g is the same as doing m with the composion of f and g” (3) (m >>= f) >>= g == m >>= (fun x -> f x >>= g) Breaking the Law Just like one expects any CONTAINER to behave in a parcular way, one has expectaons of MONADs.

Le identy: “return does nothing observable” (1) return v >>= f == f v

return 3 >>= fun x -> return x module LoggingMonad = struct == (3,”start”) >>= fun x -> return x

== (3, “start” ^ “start”) type ‘a M = ‘a * string == (3, “startstart”)

let return x = (x, “start”)

let (>>=) c f = let (v, s) = c in (fun x -> return x) 3 let (v’,s’) = f v in == return 3 (v’, s ^ s’) == (3, “start”) end Breaking the Law What are the consequences of breaking the law?

Well, if you told your friend you’ve implemented a monad and they can use it in your code, they will expect that they can rewrite their code using equaons like this one: return x >>= f == f x

If you tell your friend you’ve implemented the monad interface but none of the monad laws hold your friend will probably say: Ok, tell me what your funcons do then and please stop using the word monad because it is confusing. It is like you are claiming to have implemented the QUEUE interface but insert and remove are First-In, First-Out like a stack.

In Haskell or F# or Scala, breaking the monad laws may have more severe consequences, because the compiler actually uses those laws to do some transformaons of your code. By now you should appreciate the significance of Haskell’s logo, combining the lambda λ and the monadic bind operator >>=.

HASKELL WRAPUP A Monadic Skin

In languages like ML or Java, there is no way to disnguish between: – pure funcons with type int -> int – effecul funcons with type int -> int

In Haskell, the programmer can choose when to live in the IO monad and when to live in the realm of pure funconal programming. – Counter-point: We have shown that it is useful to be able to build pure abstracons using imperave infrastructure (eg: laziness, futures, parallel sequences, memoizaon). You can’t do that in Haskell (without escaping the type system via unsafeI0)

Interesng perspecve: It is not Haskell that lacks imperave features, but rather the other languages that lack the ability to have a stacally disnguishable pure subset.

A checked pure-impure separaon facilitates reasoning about programs, especially concurrent ones. The Central Challenge

Useful Arbitrary effects

No effects Useless

Dangerous Safe The Challenge of Effects

Plan A (everyone else) Useful Arbitrary effects Nirvana

Plan B (Haskell)

No effects Useless

Dangerous Safe Two Basic Approaches: Plan A

Arbitrary effects

Examples Default = Any effect Plan = Add restrictions • Regions • Ownership types • Rust – following from research languages like Vault, Spec#, Cyclone Two Basic Approaches: Plan B

Default = No effects Plan = Selectively permit effects

Types play a major role

Two main approaches: • Domain specific languages (SQL, Xquery, Google map/ reduce) • Wide-spectrum funconal Value oriented languages + controlled effects programming (e.g. Haskell) Lots of Cross Over

Plan A (everyone else) Useful Arbitrary effects Nirvana

Envy Plan B (Haskell)

according to Simon Peyton Jones No effects Useless

Dangerous Safe Lots of Cross Over

Plan A (everyone else) Useful Arbitrary effects Nirvana

Ideas; e.g. Software Transactional Memory Plan B (Haskell)

No effects Useless

Dangerous Safe Are we there yet?

Useful Is Haskell here? Nirvana

Or here? Plan B Some issues: (Haskell)

• Can’t encapsulate effecul computaon inside pure- funconal types. No effects • Lazy thunks can give a Useless performance penalty

Dangerous Safe An Assessment and a Predicon

One of Haskell’s most significant contributions is to take purity seriously, and relentlessly pursue Plan B.

Imperative languages will embody growing (and checkable) pure subsets.

-- Simon Peyton Jones