Bucharest FP
June 21, 2016
570

# Equational Reasoning in Programming

June 21, 2016

## Transcript

1. This slide intentionally left blank

2. Equational Reasoning
Mihai Maruseac
June 21, 2016

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.

4. Example
f = sum . take 5 . repeat
f 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

19. Functional programming encourages equational reasoning

20. Functional programming encourages equational reasoning
understanding evaluation / debugging
typeclass rules
GHC rewrite rules
refactoring
theorem proving
optimizations
algorithm design
data structure design

21. Evaluation/Debugging
evaluation: see previous example

22. Evaluation/Debugging
evaluation: see previous example
cool tool for teaching

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

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
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
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)

36. Typeclass rules
these rules are not checked by compiler

37. Typeclass rules
these rules are not checked by compiler
prove 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
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)

58. Refactoring

59. Theorem Proving
Structural induction.

60. Theorem Proving
Structural induction.
data and codata

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.
codata.

78. Hermit
Tunnel
http://ku-fpg.github.io/software/hermit/
http://www.ittc.ku.edu/~afarmer/talks/unsw.html

79. Hermit
Tunnel
http://ku-fpg.github.io/software/hermit/
http://www.ittc.ku.edu/~afarmer/talks/unsw.html
Similar: Coq, Idris

80. Hermit
Tunnel
http://ku-fpg.github.io/software/hermit/
http://www.ittc.ku.edu/~afarmer/talks/unsw.html
Similar: Coq, Idris (dependent languages)

81. Hermit
Tunnel
http://ku-fpg.github.io/software/hermit/
http://www.ittc.ku.edu/~afarmer/talks/unsw.html
Similar: Coq, Idris (dependent languages)
Proving 4 color theorem

82. Optimizations

83. Solving sudoku

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

86. Solving sudoku
solve = filter valid . expand . prune . 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

90. In other languages
lazy, functional languages: everything applies

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

104. Warning
Beware of code that is too smart.

105. Warning
Beware of code that is too smart.
2009-March/058475.html

106. Warning
Beware of code that is too smart.
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.