verifying type class laws with equational reasoning
play

Verifying Type Class Laws with Equational Reasoning A description - PowerPoint PPT Presentation

Verifying Type Class Laws with Equational Reasoning A description by example Lukas Hofmaier lukas.hofmaier@hsr.ch June 12, 2015 Progam Analysis and Transformation Seminar FS15 Outline Motivation Equational Reasoning Referential Transparency


  1. Verifying Type Class Laws with Equational Reasoning A description by example Lukas Hofmaier lukas.hofmaier@hsr.ch June 12, 2015 Progam Analysis and Transformation Seminar FS15

  2. Outline Motivation Equational Reasoning Referential Transparency Reasoning about algebraic properties Reasoning about programs Type Classes Example Proof for a Monoid Law

  3. Why I’m interested in the topic? An often touted property of pure functional languages is referential transparency.

  4. What is referential transparency? Computation yields the same value each time it is invoked y = f x g = h y y We can replace the definition of g with g = h (f x) (f x) and get the same result.

  5. What is equational reasoning? Equational reasoning is a method originally used in algebra. It’s the process of proving a given property by substituting equal expressions. ( x + a )( x + b ) = x 2 + ( a + b ) x + ab 0 Example taken from Graham Hutton, Programming in Haskell

  6. What is equational reasoning? Equational reasoning is a method originally used in algebra. It’s the process of proving a given property by substituting equal expressions. ( x + a )( x + b ) = x 2 + ( a + b ) x + ab ( x + a )( x + b ) = x 2 + ax + xb + ab (use distributivity) 0 Example taken from Graham Hutton, Programming in Haskell

  7. What is equational reasoning? Equational reasoning is a method originally used in algebra. It’s the process of proving a given property by substituting equal expressions. ( x + a )( x + b ) = x 2 + ( a + b ) x + ab ( x + a )( x + b ) = x 2 + ax + xb + ab (use distributivity) x 2 + ax + xb + ab = x 2 + ax + bx + ab (use commutativity) 0 Example taken from Graham Hutton, Programming in Haskell

  8. What is equational reasoning? Equational reasoning is a method originally used in algebra. It’s the process of proving a given property by substituting equal expressions. ( x + a )( x + b ) = x 2 + ( a + b ) x + ab ( x + a )( x + b ) = x 2 + ax + xb + ab (use distributivity) x 2 + ax + xb + ab = x 2 + ax + bx + ab (use commutativity) x 2 + ax + bx + ab = x 2 + ( a + b ) x + ab (use distributivity) 0 Example taken from Graham Hutton, Programming in Haskell

  9. Reasoning about programs We can reason the same way about programs. For instance, we want to verify the following property. length [x] = 1 Given the function definition of length . length [] = 0 length (x:xs) = 1 + length xs

  10. Reasoning about programs Definitions length [] = 0 length (x:xs) = 1 + length xs Step-by-step substitution Begin from the left-hand side length [x] and try to get to the right-hand side 1 by substituting equals-for-equals. length [x] -- [x] is the same as x:[]

  11. Reasoning about programs Definitions length [] = 0 length (x:xs) = 1 + length xs Step-by-step substitution Begin from the left-hand side length [x] and try to get to the right-hand side 1 by substituting equals-for-equals. length [x] -- [x] is the same as x:[] = length (x:[]) -- apply definition

  12. Reasoning about programs Definitions length [] = 0 length (x:xs) = 1 + length xs Step-by-step substitution Begin from the left-hand side length [x] and try to get to the right-hand side 1 by substituting equals-for-equals. length [x] -- [x] is the same as x:[] = length (x:[]) -- apply definition = 1 + length [] -- apply definition

  13. Reasoning about programs Definitions length [] = 0 length (x:xs) = 1 + length xs Step-by-step substitution Begin from the left-hand side length [x] and try to get to the right-hand side 1 by substituting equals-for-equals. length [x] -- [x] is the same as x:[] = length (x:[]) -- apply definition = 1 + length [] -- apply definition = 1 + 0 -- 1 + 0 = 1 = 1

  14. Type Classes Type classes ◮ Describes the behavior of a type. ◮ Type class is a construct that supports overloaded functions and ad hoc polymorphism ◮ An overloaded function uses different function definitions depending on the types of the arguments ◮ constrains the type variable in the type signature declaration t . t has to be a member of Show . Example showlist :: Show t => [t] -> String showlist [] = "" showlist (x:xs) = show x ++ showlist xs

  15. Relation of type classes, types and values * 1 * * type type class value

  16. Type class laws ◮ Type classes exhibit type class laws ◮ Laws ensures expected behavior (e.g. associativity) ◮ The Haskell compiler does not enforce type class laws. ◮ We can use laws to apply equational reasoning

  17. Relation of type classes with equational reasoning

  18. Example Proof for a Monoid Law Suppose we want to build a plugin system Plugin A plugin in our example is an IO action that takes a Char value and does some work with it (e.g. log to a file) It has type Char -> IO () . Usage main = do c <- getChar logto c

  19. Requirements Composability composedPlugin :: Char -> IO () composedPlugin = logto ‘mappend‘ print2stdout -- mappend :: Plugin -> Plugin -> Plugin Both arguments are of type Char -> IO () . The return value is also of type Char -> IO () . Associativity The order of evaluation must not matter. composed1 = logto ‘mappend‘ (print2stdout ‘mappend‘ donothing) composed2 = (logto ‘mappend‘ print2stdout) ‘mappend‘ donothing

  20. Monoids to the rescue There is a type class for this behavior. ◮ A monoid is when you have an associative binary function and a value which acts as an identity. Types which behave like monoids can be part of the type class Monoid . For example list [] under concatenation is a Monoid . ◮ The monoid laws state that mappend must be associative. ◮ If mappend satisfies the monoid laws then we are able to add plugins without concerning about the order of evaluation.

  21. Example Proof for a Monoid Law Instance implementation Members of the type class Monoid have to implement the functions mempty and mappend . instance (Applicative f, Monoid a) => Monoid (f a) where mempty = pure mempty mappend = liftA2 mappend Left identity law The left identity law states that mempty has to behave like the identity element with respect to mappend . mappend mempty x = x

  22. Proof of the monoid laws mappend mempty x -- apply def. mappend Example from www.haskellforall.com

  23. Proof of the monoid laws mappend mempty x -- apply def. mappend = liftA2 mappend mempty x -- apply def. mempty Example from www.haskellforall.com

  24. Proof of the monoid laws mappend mempty x -- apply def. mappend = liftA2 mappend mempty x -- apply def. mempty = liftA2 mappend (pure mempty) x -- apply def. of liftA2 Example from www.haskellforall.com

  25. Proof of the monoid laws mappend mempty x -- apply def. mappend = liftA2 mappend mempty x -- apply def. mempty = liftA2 mappend (pure mempty) x -- apply def. of liftA2 = (pure mappend <*> pure mempty) <*> x Example from www.haskellforall.com

  26. Proof of the monoid laws Applicative type class instances satisfy the following laws: ◮ pure f <*> pure x = pure (f x) ◮ pure id <*> x = x We can use this property to substitute the left-hand side. (pure mappend <*> pure mempty) <*> x

  27. Proof of the monoid laws Applicative type class instances satisfy the following laws: ◮ pure f <*> pure x = pure (f x) ◮ pure id <*> x = x We can use this property to substitute the left-hand side. (pure mappend <*> pure mempty) <*> x = pure (mappend mempty) <*> x -- transform to lambda

  28. Proof of the monoid laws Applicative type class instances satisfy the following laws: ◮ pure f <*> pure x = pure (f x) ◮ pure id <*> x = x We can use this property to substitute the left-hand side. (pure mappend <*> pure mempty) <*> x = pure (mappend mempty) <*> x -- transform to lambda = pure (\a -> mappend mempty a) <*> x -- 1. monoid law

  29. Proof of the monoid laws Applicative type class instances satisfy the following laws: ◮ pure f <*> pure x = pure (f x) ◮ pure id <*> x = x We can use this property to substitute the left-hand side. (pure mappend <*> pure mempty) <*> x = pure (mappend mempty) <*> x -- transform to lambda = pure (\a -> mappend mempty a) <*> x -- 1. monoid law = pure (\a -> a) <*> x -- a -> a = id

  30. Proof of the monoid laws Applicative type class instances satisfy the following laws: ◮ pure f <*> pure x = pure (f x) ◮ pure id <*> x = x We can use this property to substitute the left-hand side. (pure mappend <*> pure mempty) <*> x = pure (mappend mempty) <*> x -- transform to lambda = pure (\a -> mappend mempty a) <*> x -- 1. monoid law = pure (\a -> a) <*> x -- a -> a = id = pure id <*> x -- applicative law = x

  31. Conclusion ◮ Referential transparency makes it possible to conduct equational reasoning ◮ The process of proving a property is cumbersome and difficult ◮ Equational reasoning provides certainty that a program has particular properties.

  32. For Further Reading Graham Hutton, Programming in Haskell, Cambridge University Press, 2007. Gabriel Gonzales, Equational reasoning at scale, http://www.haskellforall.com .

Recommend


More recommend