Bucharest FP
June 21, 2016
630

# Equational Reasoning in Programming

June 21, 2016

## Transcript

3. ### Equational reasoning is the method of manipulating structures such as

formulas and expressions. The basic idea is that equals can be replaced by equals in any context.

10 = ?
5. ### Example f = sum . take 5 . repeat f

10 = ? f 10 = sum . take 5 . repeat \$ 10
6. ### Example f = sum . take 5 . repeat f

10 = ? f 10 = sum . take 5 . repeat \$ 10 repeat x = x : repeat x take n (x:xs) | n > 0 = x : take (n - 1) xs | otherwise = [] sum [] = 0 sum (x:xs) = x + sum xs
7. ### Example (beta reduction) repeat x = x : repeat x

take n (x:xs) | n > 0 = x : take (n - 1) xs | otherwise = [] sum [] = 0 sum (x:xs) = x + sum xs f 10 = sum (take 5 (repeat 10))
8. ### Example (beta reduction) repeat x = x : repeat x

take n (x:xs) | n > 0 = x : take (n - 1) xs | otherwise = [] sum [] = 0 sum (x:xs) = x + sum xs f 10 = sum (take 5 (repeat 10)) 5 > 0 so we take branch n > 0 of take
9. ### Example (beta reduction) repeat x = x : repeat x

take n (x:xs) | n > 0 = x : take (n - 1) xs | otherwise = [] sum [] = 0 sum (x:xs) = x + sum xs f 10 = sum (take 5 (repeat 10)) 5 > 0 so we take branch n > 0 of take Pattern match on (x:xs) so, expand repeat first
10. ### Example (beta reduction) repeat x = x : repeat x

take n (x:xs) | n > 0 = x : take (n - 1) xs | otherwise = [] sum [] = 0 sum (x:xs) = x + sum xs f 10 = sum (take 5 (repeat 10)) 5 > 0 so we take branch n > 0 of take Pattern match on (x:xs) so, expand repeat first
11. ### Example (beta reduction) repeat x = x : repeat x

take n (x:xs) | n > 0 = x : take (n - 1) xs | otherwise = [] sum [] = 0 sum (x:xs) = x + sum xs f 10 = sum (take 5 (repeat 10)) 5 > 0 so we take branch n > 0 of take Pattern match on (x:xs) so, expand repeat first f 10 = sum (take 5 (10 : repeat 10))
12. ### Example (beta reduction) repeat x = x : repeat x

take n (x:xs) | n > 0 = x : take (n - 1) xs | otherwise = [] sum [] = 0 sum (x:xs) = x + sum xs f 10 = sum (take 5 (repeat 10)) 5 > 0 so we take branch n > 0 of take Pattern match on (x:xs) so, expand repeat first f 10 = sum (take 5 (10 : repeat 10)) f 10 = sum (10 : take 4 (repeat 10))
13. ### Example repeat x = x : repeat x take n

(x:xs) | n > 0 = x : take (n - 1) xs | otherwise = [] sum [] = 0 sum (x:xs) = x + sum xs f 10 = sum (10 : take 4 (repeat 10))
14. ### Example repeat x = x : repeat x take n

(x:xs) | n > 0 = x : take (n - 1) xs | otherwise = [] sum [] = 0 sum (x:xs) = x + sum xs f 10 = sum (10 : take 4 (repeat 10)) Multiple possibilities to evaluate the expression
15. ### Example repeat x = x : repeat x take n

(x:xs) | n > 0 = x : take (n - 1) xs | otherwise = [] sum [] = 0 sum (x:xs) = x + sum xs f 10 = sum (10 : take 4 (repeat 10)) Multiple possibilities to evaluate the expression lazy vs eager evaluation
16. ### Diamond theorem Excluding ⊥, all evaluation paths lead to the

same result. Diamond theorem, Church-Rosser exp1 exp2 exp1 arg fun exp2 fun arg res
17. ### Example repeat x = x : repeat x take n

(x:xs) | n > 0 = x : take (n - 1) xs | otherwise = [] sum [] = 0 sum (x:xs) = x + sum xs f 10 = sum (10 : take 4 (repeat 10))
18. ### Example repeat x = x : repeat x take n

(x:xs) | n > 0 = x : take (n - 1) xs | otherwise = [] sum [] = 0 sum (x:xs) = x + sum xs f 10 = sum (10 : take 4 (repeat 10)) f 10 = sum (10 : take 4 (repeat 10)) f 10 = sum (10 : 10 : take 3 (repeat 10)) f 10 = sum (10 : 10 : 10 : take 2 (repeat 10)) f 10 = sum (10 : 10 : 10 : 10 : take 1 (repeat 10)) f 10 = sum (10 : 10 : 10 : 10 : 10 : take 0 (repeat 10)) f 10 = sum [10, 10, 10, 10, 10] f 10 = 50

20. ### Functional programming encourages equational reasoning understanding evaluation / debugging typeclass

rules GHC rewrite rules refactoring theorem proving optimizations algorithm design data structure design

23. ### Evaluation/Debugging evaluation: see previous example cool tool for teaching hard

to do for big programs
24. ### Evaluation/Debugging evaluation: see previous example cool tool for teaching hard

to do for big programs lazy evaluation vs. stack-based debugging
25. ### Evaluation/Debugging evaluation: see previous example cool tool for teaching hard

to do for big programs lazy evaluation vs. stack-based debugging GHCi debugger
26. ### Evaluation/Debugging evaluation: see previous example cool tool for teaching hard

to do for big programs lazy evaluation vs. stack-based debugging GHCi debugger hard to use for large programs
27. ### Evaluation/Debugging evaluation: see previous example cool tool for teaching hard

to do for big programs lazy evaluation vs. stack-based debugging GHCi debugger hard to use for large programs Hood: Haskell Object Observation Debugger
28. ### Evaluation/Debugging evaluation: see previous example cool tool for teaching hard

to do for big programs lazy evaluation vs. stack-based debugging GHCi debugger hard to use for large programs Hood: Haskell Object Observation Debugger print intermediate data structure
29. ### Evaluation/Debugging evaluation: see previous example cool tool for teaching hard

to do for big programs lazy evaluation vs. stack-based debugging GHCi debugger hard to use for large programs Hood: Haskell Object Observation Debugger print intermediate data structure use equational reasoning to reason about state around bug
30. ### Typeclass rules class Functor f where fmap :: (a ->

b) -> f a -> f b
31. ### Typeclass rules class Functor f where fmap :: (a ->

b) -> f a -> f b fmap id = id fmap (f . g) = fmap f . fmap g
32. ### Typeclass rules class (Functor f) => Applicative f where pure

:: a -> f a (<*>) :: f (a -> b) -> f a -> f b
33. ### Typeclass rules class (Functor f) => Applicative f where pure

:: a -> f a (<*>) :: f (a -> b) -> f a -> f b pure f <*> x = fmap f x pure id <*> v = v pure (.) <*> u <*> v <*> w = u <*> (v <*> w) pure f <*> pure x = pure (f x) u <*> pure y = pure (\$ y) <*> u
34. ### Typeclass rules class Monoid m where mempty :: m mappend

:: m -> m -> m mconcat :: [m] -> m mconcat = foldr mappend mempty (<>) = mappend
35. ### Typeclass rules class Monoid m where mempty :: m mappend

:: m -> m -> m mconcat :: [m] -> m mconcat = foldr mappend mempty (<>) = mappend mempty <> x = x x <> mempty = x (x <> y) <> z = x <> (y <> z)

them
38. ### Typeclass rules these rules are not checked by compiler prove

them test with QuickCheck or similar
39. ### Typeclass rules these rules are not checked by compiler prove

them test with QuickCheck or similar http://austinrochford.com/posts/ 2014-05-27-quickcheck-laws.html
40. ### Typeclass rules these rules are not checked by compiler prove

them test with QuickCheck or similar http://austinrochford.com/posts/ 2014-05-27-quickcheck-laws.html parametricity makes some rules be always valid (Functor)
41. ### Typeclass rules these rules are not checked by compiler prove

them test with QuickCheck or similar http://austinrochford.com/posts/ 2014-05-27-quickcheck-laws.html parametricity makes some rules be always valid (Functor) these rules hold over all instances of typeclass
42. ### Typeclass rules these rules are not checked by compiler prove

them test with QuickCheck or similar http://austinrochford.com/posts/ 2014-05-27-quickcheck-laws.html parametricity makes some rules be always valid (Functor) these rules hold over all instances of typeclass same reasoning holds when moving from Maybe a to [a]
43. ### Typeclass rules these rules are not checked by compiler prove

them test with QuickCheck or similar http://austinrochford.com/posts/ 2014-05-27-quickcheck-laws.html parametricity makes some rules be always valid (Functor) these rules hold over all instances of typeclass same reasoning holds when moving from Maybe a to [a] allow higher-level reasoning
44. ### Typeclass rules these rules are not checked by compiler prove

them test with QuickCheck or similar http://austinrochford.com/posts/ 2014-05-27-quickcheck-laws.html parametricity makes some rules be always valid (Functor) these rules hold over all instances of typeclass same reasoning holds when moving from Maybe a to [a] allow higher-level reasoning easily change the semantics of the program with minimal impact on performance/accuracy/etc.
45. ### GHC rewrite rules {-# RULES "map/map" forall f g xs.

map f (map g xs) = map (f.g) xs #-}
46. ### GHC rewrite rules {-# RULES "map/map" forall f g xs.

map f (map g xs) = map (f.g) xs #-} map f . map g = map (f . g)
47. ### GHC rewrite rules {-# RULES "map/map" forall f g xs.

map f (map g xs) = map (f.g) xs #-} map f . map g = map (f . g) List fusion function generating list function consuming list to generate result list = temporary data structure why not eliminate it?
48. ### List fusion :: Good producers List comprehensions Enumerations of simple

types Explicit lists The (:) constructor
49. ### List fusion :: Good producers List comprehensions Enumerations of simple

types Explicit lists The (:) constructor (++)
50. ### List fusion :: Good producers List comprehensions Enumerations of simple

types Explicit lists The (:) constructor (++) map take, drop, filter iterate, repeat zip, zipWith
51. ### List fusion :: Good consumers List comprehensions array (++) foldr

sum, product, all, any map take, drop, filter concat unzip zip, zipWith partition head sortBy
52. ### GHC rewrite rules (2) What if some equation is valid

only for one type?
53. ### GHC rewrite rules (2) What if some equation is valid

only for one type? or effective only for some types
54. ### GHC rewrite rules (2) What if some equation is valid

only for one type? or effective only for some types specialize rules
55. ### GHC rewrite rules (2) What if some equation is valid

only for one type? or effective only for some types specialize rules
56. ### GHC rewrite rules (2) What if some equation is valid

only for one type? or effective only for some types specialize rules toDouble :: Real a => a -> Double toDouble = fromRational . toRational
57. ### GHC rewrite rules (2) What if some equation is valid

only for one type? or effective only for some types specialize rules toDouble :: Real a => a -> Double toDouble = fromRational . toRational {-# RULES "toDouble/Int" toDouble = i2d #-} i2d (I# i) = D# (int2Double# i)

61. ### Theorem Proving Structural induction. data and codata final algebras and

final coalgebras
62. ### Theorem Proving Structural induction. data and codata final algebras and

final coalgebras
63. ### Theorem Proving Structural induction. data and codata final algebras and

final coalgebras sum [] = 0 sum (a:as) = a + sum as
64. ### Theorem Proving Structural induction. data and codata final algebras and

final coalgebras sum [] = 0 sum (a:as) = a + sum as sumSoFar x [] = [x] sumSoFar x (y:ys) = x : sumSoFar (x+y) ys
65. ### Data and (Structural) Induction sum [] = 0 sum (a:as)

= a + sum as sum (map (+1) x) = length x + sum x
66. ### Data and (Structural) Induction sum [] = 0 sum (a:as)

= a + sum as sum (map (+1) x) = length x + sum x Base case:
67. ### Data and (Structural) Induction sum [] = 0 sum (a:as)

= a + sum as sum (map (+1) x) = length x + sum x Base case: [] sum (map (+1) []) = sum [] = 0 length [] + sum [] = 0 + 0 = 0
68. ### Data and (Structural) Induction sum [] = 0 sum (a:as)

= a + sum as sum (map (+1) x) = length x + sum x Base case: [] sum (map (+1) []) = sum [] = 0 length [] + sum [] = 0 + 0 = 0 Inductive case:
69. ### Data and (Structural) Induction sum [] = 0 sum (a:as)

= a + sum as sum (map (+1) x) = length x + sum x Base case: [] sum (map (+1) []) = sum [] = 0 length [] + sum [] = 0 + 0 = 0 Inductive case:(for all constructors) sum (map (+1) (x:xs)) = sum ((x+1) : map (+1) xs) = (x+1) + sum (map (+1) xs) = (x+1) + length xs + sum xs = (1 + length xs) + (x + sum xs) = length (x:xs) + sum (x:xs)
70. ### Codata and (Structural) Coinduction fib = 1 : 1 :

zipWith (+) fib (tail fib) luc = 2 : 1 : zipWith (+) luc (tail luc)
71. ### Codata and (Structural) Coinduction fib = 1 : 1 :

zipWith (+) fib (tail fib) luc = 2 : 1 : zipWith (+) luc (tail luc) 1, 1, 2, 3, 5, 8, 13, 21, 34 2, 1, 3, 4, 7, 11, 18, 29, 47
72. ### Codata and (Structural) Coinduction fib = 1 : 1 :

zipWith (+) fib (tail fib) luc = 2 : 1 : zipWith (+) luc (tail luc) 1, 1, 2, 3, 5, 8, 13, 21, 34 2, 1, 3, 4, 7, 11, 18, 29, 47 Ln+2 = Fn+1 + 2 ∗ Fn
73. ### Codata and (Structural) Coinduction fib = 1 : 1 :

zipWith (+) fib (tail fib) luc = 2 : 1 : zipWith (+) luc (tail luc) 1, 1, 2, 3, 5, 8, 13, 21, 34 2, 1, 3, 4, 7, 11, 18, 29, 47 Ln+2 = Fn+1 + 2 ∗ Fn luc !! (n+2) = fib !! (n+1) + 2 * fib !! n
74. ### Codata and (Structural) Coinduction fib = 1 : 1 :

zipWith (+) fib (tail fib) luc = 2 : 1 : zipWith (+) luc (tail luc) 1, 1, 2, 3, 5, 8, 13, 21, 34 2, 1, 3, 4, 7, 11, 18, 29, 47 Ln+2 = Fn+1 + 2 ∗ Fn luc !! (n+2) = fib !! (n+1) + 2 * fib !! n tail (tail luc) = zipWith (+) (tail fib) (map (*2) fib)
75. ### Codata and (Structural) Coinduction fib = 1 : 1 :

zipWith (+) fib (tail fib) luc = 2 : 1 : zipWith (+) luc (tail luc) tail (tail luc) = zipWith (+) (tail fib) (map (*2) fib) zipWith (+) (tail fib) (map (*2) fib) = zipWith (+) (1 : zipWith (+) fib (tail fib)) (2 : 2 : map (*2) (zipWith (+)fib (tail fib))) = 3 : zipWith (+) (zipWith (+) fib (tail fib)) (2 : map (*2) (zipWith (+) fib (tail fib))) = ...
76. ### More on codata Firstly, induction principles are well known and

much used. The coinductive definition and proof principles for coalgebras are less well known by far, and often even not very clearly formulated. Rutten, 2000, seminal paper
77. ### More on codata Firstly, induction principles are well known and

much used. The coinductive definition and proof principles for coalgebras are less well known by far, and often even not very clearly formulated. Rutten, 2000, seminal paper bisimilarity, bisimulation A property holds by induction if there is good reason for it to hold; whereas a property holds by coinduction if there is no good reason for it not to hold. Induction is about finite data, coinduction is about infinite codata.

Coq, Idris
80. ### Hermit Haskell Equational Reasoning Model-to-Implementation Tunnel http://ku-fpg.github.io/software/hermit/ https://hackage.haskell.org/package/hermit http://www.ittc.ku.edu/~afarmer/talks/unsw.html Similar:

Coq, Idris (dependent languages)
81. ### Hermit Haskell Equational Reasoning Model-to-Implementation Tunnel http://ku-fpg.github.io/software/hermit/ https://hackage.haskell.org/package/hermit http://www.ittc.ku.edu/~afarmer/talks/unsw.html Similar:

Coq, Idris (dependent languages) Proving 4 color theorem

84. ### Solving sudoku type Matrix a = [Row a] type Row

a = [a] type Grid = Matrix Digit type Digit = Char digits = [’1’..’9’] blank = (== ’0’) solve = filter valid . expand . choices
85. ### Solving sudoku type Matrix a = [Row a] type Row

a = [a] type Grid = Matrix Digit type Digit = Char digits = [’1’..’9’] blank = (== ’0’) solve = filter valid . expand . choices 147 808 829 414 345 923 316 083 210 206 383 297 601

. choices
87. ### Solving sudoku solve = filter valid . expand . prune

. choices 12 157 665 459 056 928 801
88. ### Solving sudoku solve = filter valid . expand . prune

. choices 12 157 665 459 056 928 801 After some calculation solve = search . choices search m | not (safe m) = [] | complete m’ = [map (map (head) m’] | otherwise = concat (map search (expand1 m’)) where m’ = prune m
89. ### Solving sudoku solve = filter valid . expand . prune

. choices 12 157 665 459 056 928 801 After some calculation solve = search . choices search m | not (safe m) = [] | complete m’ = [map (map (head) m’] | otherwise = concat (map search (expand1 m’)) where m’ = prune m Blazingly fast: 8s on all puzzles

91. ### In other languages lazy, functional languages: everything applies eager, functional

languages: beware ⊥
92. ### In other languages lazy, functional languages: everything applies eager, functional

languages: beware ⊥ non-functional languages: possible?
93. ### In other languages lazy, functional languages: everything applies eager, functional

languages: beware ⊥ non-functional languages: possible?
94. ### In other languages lazy, functional languages: everything applies eager, functional

languages: beware ⊥ non-functional languages: possible? C++ code for cypto code on CUDA
95. ### In other languages lazy, functional languages: everything applies eager, functional

languages: beware ⊥ non-functional languages: possible? C++ code for cypto code on CUDA C++ templates
96. ### In other languages lazy, functional languages: everything applies eager, functional

languages: beware ⊥ non-functional languages: possible? C++ code for cypto code on CUDA C++ templates compilation: 24 hours
97. ### In other languages lazy, functional languages: everything applies eager, functional

languages: beware ⊥ non-functional languages: possible? C++ code for cypto code on CUDA C++ templates compilation: 24 hours running time: 500ms
98. ### In other languages lazy, functional languages: everything applies eager, functional

languages: beware ⊥ non-functional languages: possible? C++ code for cypto code on CUDA C++ templates compilation: 24 hours running time: 500ms equational reasoning to translate to non-template version
99. ### In other languages lazy, functional languages: everything applies eager, functional

languages: beware ⊥ non-functional languages: possible? C++ code for cypto code on CUDA C++ templates compilation: 24 hours running time: 500ms equational reasoning to translate to non-template version compile time: 10s
100. ### In other languages lazy, functional languages: everything applies eager, functional

languages: beware ⊥ non-functional languages: possible? C++ code for cypto code on CUDA C++ templates compilation: 24 hours running time: 500ms equational reasoning to translate to non-template version compile time: 10s running time: 550ms
101. ### In other languages lazy, functional languages: everything applies eager, functional

languages: beware ⊥ non-functional languages: possible? C++ code for cypto code on CUDA C++ templates compilation: 24 hours running time: 500ms equational reasoning to translate to non-template version compile time: 10s running time: 550ms
102. ### In other languages lazy, functional languages: everything applies eager, functional

languages: beware ⊥ non-functional languages: possible? C++ code for cypto code on CUDA C++ templates compilation: 24 hours running time: 500ms equational reasoning to translate to non-template version compile time: 10s running time: 550ms lack of tooling → every calculation must be done by hand
103. ### In other languages lazy, functional languages: everything applies eager, functional

languages: beware ⊥ non-functional languages: possible? C++ code for cypto code on CUDA C++ templates compilation: 24 hours running time: 500ms equational reasoning to translate to non-template version compile time: 10s running time: 550ms lack of tooling → every calculation must be done by hand IDE & automatic refactorings rarely help

106. ### Warning Beware of code that is too smart. https://mail.haskell.org/pipermail/haskell-cafe/ 2009-March/058475.html

different versions of same code different coding styles and levels of Haskell knowledge timings between versions derivation of efficient solution
107. ### Warning Beware of code that is too smart. https://mail.haskell.org/pipermail/haskell-cafe/ 2009-March/058475.html

different versions of same code different coding styles and levels of Haskell knowledge timings between versions derivation of efficient solution and a not-so-trivial bug
108. ### Clever Perl code is what you hope you understood in

the past, when you wrote it; clever Haskell code is what you hope you’ll understand in the future, when you’ll write it yourself! Sjur Gjøstein Karevoll, paraphrased