Pro Yearly is on sale from $80 to $50! »

Equational Reasoning in Programming

Equational Reasoning in Programming

0286822f506fc4621bd3ea0bcbfef238?s=128

Bucharest FP

June 21, 2016
Tweet

Transcript

  1. This slide intentionally left blank

  2. None
  3. Equational Reasoning Mihai Maruseac June 21, 2016

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

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

    10 = ? f 10 = sum . take 5 . repeat $ 10
  7. 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
  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))
  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
  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
  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))
  13. 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))
  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))
  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
  16. 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
  17. Diamond theorem Excluding ⊥, all evaluation paths lead to the

    same result. Diamond theorem, Church-Rosser exp1 exp2 exp1 arg fun exp2 fun arg res
  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))
  19. 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

  21. Functional programming encourages equational reasoning understanding evaluation / debugging typeclass

    rules GHC rewrite rules refactoring theorem proving optimizations algorithm design data structure design
  22. Evaluation/Debugging evaluation: see previous example

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

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

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

    to do for big programs lazy evaluation vs. stack-based debugging
  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
  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 Hood: Haskell Object Observation Debugger
  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
  30. 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
  31. Typeclass rules class Functor f where fmap :: (a ->

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

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

    :: a -> f a (<*>) :: f (a -> b) -> f a -> f b
  34. 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
  35. Typeclass rules class Monoid m where mempty :: m mappend

    :: m -> m -> m mconcat :: [m] -> m mconcat = foldr mappend mempty (<>) = mappend
  36. 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)
  37. Typeclass rules these rules are not checked by compiler

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

    them
  39. Typeclass rules these rules are not checked by compiler prove

    them test with QuickCheck or similar
  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
  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)
  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
  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]
  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
  45. 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.
  46. GHC rewrite rules {-# RULES "map/map" forall f g xs.

    map f (map g xs) = map (f.g) xs #-}
  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)
  48. 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?
  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 (++)
  51. List fusion :: Good producers List comprehensions Enumerations of simple

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

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

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

    only for one type? or effective only for some types
  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
  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
  58. 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)
  59. Refactoring HaRe: the Haskell Refactorer.

  60. Theorem Proving Structural induction.

  61. Theorem Proving Structural induction. data and codata

  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
  64. Theorem Proving Structural induction. data and codata final algebras and

    final coalgebras sum [] = 0 sum (a:as) = a + sum as
  65. 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
  66. Data and (Structural) Induction sum [] = 0 sum (a:as)

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

    = a + sum as sum (map (+1) x) = length x + sum x Base case:
  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
  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:
  70. 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)
  71. Codata and (Structural) Coinduction fib = 1 : 1 :

    zipWith (+) fib (tail fib) luc = 2 : 1 : zipWith (+) luc (tail luc)
  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
  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
  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
  75. 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)
  76. 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))) = ...
  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
  78. 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.
  79. 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

  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
  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)
  82. 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
  83. Optimizations

  84. Solving sudoku

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

    . choices
  88. Solving sudoku solve = filter valid . expand . prune

    . choices 12 157 665 459 056 928 801
  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
  90. 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

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

    languages: beware ⊥
  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?
  95. In other languages lazy, functional languages: everything applies eager, functional

    languages: beware ⊥ non-functional languages: possible? C++ code for cypto code on CUDA
  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
  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
  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
  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
  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
  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
  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
  104. 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
  105. Warning Beware of code that is too smart.

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

  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
  108. 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
  109. 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