Paper by John Hughes
Presented by Andrew Sinclair
John Hughes
Haskell
QuickCheck
Structure
Easy to write
Easy to debug
Reusable
Higher-order functions
Lazy evaluation
Assignments
Side-effects
Flow of control
listof * ::= Nil | Cons * (listof *)
Cons 42 (Cons 69 (Cons 613 Nil))
sum Nil = 0
sum (Cons n list) = n + sum list
(foldr f x) Nil = x
(foldr f x) (Cons a l) = f a ((foldr f x) l)
product = foldr (*) 1
anytrue = foldr (V) False
alltrue = foldr (^) True
doubleall = foldr doubleandcons Nil
doubleandcons n list = Cons (2 * n) list
(f . g) h = f (g h)
doubleandcons = fandcons double
double n = 2 * n
fandcons f el list = Cons (f el) list
or,
fandcons f = Cons . f
doubleall = foldr (Cons . double) Nil
doubleall = map double
giving us:
map f = foldr (Cons . f) Nil
summatrix = sum . map sum
treeof * ::= Node * (listof (treeof *))
foldtree f g a (Node label subtrees) =
f label (foldtree f g a subtrees)
foldtree f g a (Cons subtree rest) =
g (foldtree f g a subtree) (foldtree f g a rest)
foldtree f g a Nil = a
sumtree = foldtree (+) (+) 0
maptree f = foldtree (Node . f) Cons Nil
f and g
(g . f) input
g (f input)
What if f is non-terminating?
Procedural → Infinite loop :(
Functional → Lazy evaluation :D
No, because side-effects
ai+1 = (ai + n/ai)/2
If approximations converge,
a = sqrt(n)
next n x = (x + n/x) / 2
repeat f a = Cons a (repeat f (f a))
repeat (next n) a0
within eps (Cons a (Cons b rest))
= b, if abs(a - b) <= eps
= within eps (Cons b rest), otherwise
sqrt a0 eps n = within eps (repeat (next n) a0)
Numerical Differentiation
Numerical Integration
An example on refactoring
// Assume moves takes current state and returns all next states
// Assume static takes a position and calculates a number
reptree f a = Node a (map (reptree f) (f a))
gametree p = reptree moves p
maximize (Node n Nil) = n
maximize (Node n sub) = max (map minimize sub)
minimize (Node n Nil) = n
minimize (Node n sub) = max (map maximize sub)
// this doesn't work on infinite trees...
evaluate = maximize . maptree static . gametree
prune 0 (Node a x) = Node a Nil
prune (n + 1) (Node a x) = Node a (map (prune n) x)
// this will prune the search to depth 5
evaluate = maximize . maptree static . prune 5 . gametree
//Suppose we refactor maximize and minimize:
minleq Nil pot = False
minleq (Cons n rest) pot = True, if n <= pot
= minleq rest pot, otherwise
omit pot Nil = Nil
omit pot (Cons nums rest)
= omit post rest, if minleq nums pot
= Cons (min nums) (omit (min nums) rest), otherwise
mapmin (Cons nums rest) = Cons (min nums (omit (min nums) rest)
maximize' (Node n Nil) = Cons n Nil
maximize' (Node n l) = mapmin (map minimize' l)
// This will ignore branches smaller than potential minima
evaluate = max . maximize' . maptree static . prune 8 . gametree
//Assume sort and not are defined.
highfirst (Node n sub) = Node n (sort higher (map lowfirst sub))
lowfirst (Node n sub) = Node n (sort (not . higher) (map highfirst sub))
higher (Node n1 sub1)(Node n2 sub2) = n1 > n2
//This prioritizes branch expansion:
evaluate = max . maximize' . highfirst . maptree static . prune 8 . gametree
Modularity is key
Gluing solutions > scope rules
Expressiveness
Immutable Data
Pattern Matching
Easier to learn
Haskell
Clojure
Elixir
F#