
Haskell a lazy, purely func1onal 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, func1onal programming language • Like OCaml in that: – it is a func1onal language with parametric polymorphism • Unlike OCaml in that it is: – pure: func1ons 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 sophis1cated type system with higher kinds – has several cool extensions for concurrent and parallel programming including transac1onal memory Glasgow in the 1990s ghc stands for “Glasgow Haskell Compiler” Phil Wadler Simon Peyton Jones (now at MicrosoZ 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 func1onal programming languages from Scotland? HASKELL BASICS Haskell Defini1ons • Mostly like OCaml, with a few small syntac1c differences – parameters are immutable by default – let declaraons introduce new func1ons and value foo z = let triple x = x*3 in triple z – equivalently, we might use a "where" defini1on: foo z = triple z where triple x = x*3 Haskell Indentaon • Haskell, like Python, but unlike Java, OCaml or math wrihen in a notebook, has seman1cally 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 wrihen in a notebook, has seman1cally 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 op1on of declaring the type of a defini1on prior to the defini1on 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 wrihen [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 ] ] Func1ons over lists -- Sum the elements of a list listSum :: [ Integer ] -> Integer listSum [ ] = 0 listSum (x:xs) = x + listSum xs common to write funcons using mul1ple clauses, where each clause matches on a different case Func1ons deconstruc1ng lists -- Sum the elements of a list listSum :: [ Integer ] -> Integer listSum [ ] = 0 listSum (x:xs) = x + listSum xs lower case leher = any type at all (a type variable) upper case leher = a concrete type length :: [a] -> Int length [ ] = 0 length (x:xs) = 1 + length xs Func1ons deconstruc1ng 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 Func1ons deconstruc1ng 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 Subs1tu1on of Equals for Equals • A key law about Haskell programs: let x = <exp> in ... <exp> ... <exp> ... ... 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 1me; (4 `div` 2) will be evaluated twice instead of once. Subs1tu1on of Equals for Equals • We'd also like to use func1onal abstrac1on 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) (subs1tu1on) = (halve 4) + (halve 4) + y (arithme1c) = 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 func1ons 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 = <exp> in ... x ... x ... ≠ ... <exp> ... <exp> ... – In general, in Haskell, let x = <exp> in ... x ... x ... = ... <exp> ... <exp> ... 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 func1ons can no longer be described exclusively in This is kind of magical! terms of the relaonship between arguments and results Haskell has subs1tu1on of – many, many fewer equals for equals equaonal laws hold – In general, in AND ALSO HAS EFFECTS. OCaml, But how? let x = <exp> in ... x ... x ... ≠ ... <exp> ... <exp> ... – In general, in Haskell, let x = <exp> in ... x ... x ... = ... <exp> ... <exp> ... 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, hhp://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 func1onal programming guard (p a b c) + lazy evaluaon + lists return (a,b,c) + a nice notaon for monadic computa/ons Now, let’s “unpack” this to see how that works. Source: Greg Bacon, hhp://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 func1ons defined by each statement. Source: Greg Bacon, hhp://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 func1on 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.
Details
-
File Typepdf
-
Upload Time-
-
Content LanguagesEnglish
-
Upload UserAnonymous/Not logged-in
-
File Pages108 Page
-
File Size-