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

verifying type class laws with equational reasoning
SMART_READER_LITE
LIVE PREVIEW

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


slide-1
SLIDE 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

slide-2
SLIDE 2

Outline

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

slide-3
SLIDE 3

Why I’m interested in the topic?

An often touted property of pure functional languages is referential transparency.

slide-4
SLIDE 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.

slide-5
SLIDE 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) = x2 + (a + b)x + ab

0Example taken from Graham Hutton, Programming in Haskell

slide-6
SLIDE 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) = x2 + (a + b)x + ab (x + a)(x + b) = x2 + ax + xb + ab (use distributivity)

0Example taken from Graham Hutton, Programming in Haskell

slide-7
SLIDE 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) = x2 + (a + b)x + ab (x + a)(x + b) = x2 + ax + xb + ab (use distributivity) x2 + ax + xb + ab = x2 + ax + bx + ab (use commutativity)

0Example taken from Graham Hutton, Programming in Haskell

slide-8
SLIDE 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) = x2 + (a + b)x + ab (x + a)(x + b) = x2 + ax + xb + ab (use distributivity) x2 + ax + xb + ab = x2 + ax + bx + ab (use commutativity) x2 + ax + bx + ab = x2 + (a + b)x + ab (use distributivity)

0Example taken from Graham Hutton, Programming in Haskell

slide-9
SLIDE 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

slide-10
SLIDE 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:[]
slide-11
SLIDE 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
slide-12
SLIDE 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
slide-13
SLIDE 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

slide-14
SLIDE 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

slide-15
SLIDE 15

Relation of type classes, types and values

type type class value * * 1 *

slide-16
SLIDE 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

slide-17
SLIDE 17

Relation of type classes with equational reasoning

slide-18
SLIDE 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

slide-19
SLIDE 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

slide-20
SLIDE 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.

slide-21
SLIDE 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

slide-22
SLIDE 22

Proof of the monoid laws

mappend mempty x

  • - apply def. mappend

Example from www.haskellforall.com

slide-23
SLIDE 23

Proof of the monoid laws

mappend mempty x

  • - apply def. mappend

= liftA2 mappend mempty x

  • - apply def. mempty

Example from www.haskellforall.com

slide-24
SLIDE 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

slide-25
SLIDE 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

slide-26
SLIDE 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

slide-27
SLIDE 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
slide-28
SLIDE 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

slide-29
SLIDE 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
slide-30
SLIDE 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

slide-31
SLIDE 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.

slide-32
SLIDE 32

For Further Reading

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

slide-33
SLIDE 33

Why use equational reasoning?

Example Property

Correctness of a program can be specified in the form of an equation. fmap id = id (1) We can use equational reasoning to verify if a property holds.

Goals of verification

◮ Provide certainty that a program has particular properties ◮ Contributes to the reliability and robustness of a software

Examples of use

Example of use

◮ the first formally verified micro kernel, seL4. ◮ streaming library ”pipes”

slide-34
SLIDE 34

Comparison of testing and proving properties

Suppose we want to check if the following equations holds reverse reverse xs = xs How do we check that?

◮ Testing ◮ Property-based testing ◮ Proof that property holds

slide-35
SLIDE 35

Testing

In order to check the behavior, a function evaluates both sides of equation and compares the values input = [1,2,3] test_reverse :: Bool test_reverse = reverse (reverse input) == input

slide-36
SLIDE 36

Comparison of testing and proving properties

Testing Code is compiled and executed. Can expose error. Cannot proof absence of errors. Proof Properties hold under all circumstances.

slide-37
SLIDE 37

Comparison of testing and proving properties

slide-38
SLIDE 38

Proof by structural induction

Base case Prove p(0) is true. Induction step Prove p(n + 1) if p(n) (induction hypothesis) is true.

slide-39
SLIDE 39

Example proof by structural induction

The length of two concatenated lists xs and ys, is the same as the sum of the length of xs and the length of ys length (xs ++ ys) = length xs + length ys

slide-40
SLIDE 40

Base case

We have to show that the property holds for the base case, the empty list [] length ([] ++ ys) = length [] + length ys

Left-hand side

length ([] ++ ys) = length ys

Right-hand side

length [] + length ys = 0 + length ys = length ys

slide-41
SLIDE 41

Induction step

We have to show that if the equation holds for any list xs then it also holds for x:xs pplength ([] ++ ys) = length [] + length ys

Left-hand side

length ((x:xs) ++ ys) = length (x:(xs ++ ys)) = 1 + length (xs ++ ys) = 1 + length xs + length ys

Right-hand side

length (x:xs) + length ys 1 + length xs + length ys length [] + length ys

slide-42
SLIDE 42

An Applicative can be a monoid

slide-43
SLIDE 43

Reusability of proof

General implementation for mappend. If f is an Applicative and b is a Monoid then f b is also a Monoid {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE OverlappingInstances #-} instance (Applicative f, Monoid a) => Monoid (f a) where mempty = pure mempty mappend = liftA2 mappend

◮ To prove a type class law we can use equational reasoning. ◮ Type classes allow us to generalize definitions. A proof for the

generalization is valid for all specializations.

slide-44
SLIDE 44

Generalization of Proof

  • 1. The standard library provides a Monoid instance for ()
  • 2. IO () is a Monoid because IO is part of the Applicative

type class and () is a Monoid. The type (->) r (that’s the type of Haskell functions) is a part of the Applicative type class.

  • 3. Char -> IO () is a Monoid because (->) Char is an

Applicative and IO () is a Monoid.