Signature Inference for Functional Property Discovery or: How never - - PowerPoint PPT Presentation

signature inference for functional property discovery
SMART_READER_LITE
LIVE PREVIEW

Signature Inference for Functional Property Discovery or: How never - - PowerPoint PPT Presentation

Signature Inference for Functional Property Discovery or: How never to come up with tests manually anymore(*) Tom Sydney Kerckhove ETH Zurich https://cs-syd.eu/ https://github.com/NorfairKing 27 July 2017 Long term vision: A future in which


slide-1
SLIDE 1

Signature Inference for Functional Property Discovery

  • r: How never to come up with tests manually anymore(*)

Tom Sydney Kerckhove

ETH Zurich https://cs-syd.eu/ https://github.com/NorfairKing

27 July 2017

slide-2
SLIDE 2

Long term vision: A future in which ...

slide-3
SLIDE 3

Long term vision: A future in which ...

Software works

slide-4
SLIDE 4

Long term vision: A future in which ...

Software works because is cheaper to make software that works

slide-5
SLIDE 5

Long term vision: A future in which ...

Software works because is cheaper to make software that works, even in the short term.

slide-6
SLIDE 6

Long term goal:

We never come up with tests manually.

slide-7
SLIDE 7

Motivation

slide-8
SLIDE 8

Motivation

Writing correct software is hard for humans.

slide-9
SLIDE 9

Idea

slide-10
SLIDE 10

Motivation

Make machines do it!

slide-11
SLIDE 11

Idea

slide-12
SLIDE 12

Motivation

I will write the code myself, and get the machine to prove that it is correct.

slide-13
SLIDE 13

Idea

slide-14
SLIDE 14

Motivation

I will write the code myself, and get the machine to test that it works.

slide-15
SLIDE 15

Making machines test that my code works

sort [4, 1, 6] == [1, 4, 6]

slide-16
SLIDE 16

Making machines test that my code works

sort [4, 1, 6] == [1, 4, 6]

slide-17
SLIDE 17

Making machines test that my code works

sort [4, 1, 6] == [1, 4, 6]

slide-18
SLIDE 18

Fixing the coverage problem

slide-19
SLIDE 19

Property testing

forAll arbitrary $ \ls -> isSorted (sort ls)

slide-20
SLIDE 20

Property testing

forAll arbitrary $ \ls -> isSorted (sort ls)

slide-21
SLIDE 21

Property testing

forAll arbitrary $ \ls -> isSorted (sort ls)

slide-22
SLIDE 22

Fixing the cost problem

slide-23
SLIDE 23

Property Discovery

forAll arbitrary $ \ls -> sort ls == ls

slide-24
SLIDE 24

Property Discovery with QuickSpec

slide-25
SLIDE 25

Example code

module MySort where mySort :: Ord a => [a] -> [a] mySort [] = [] mySort (x:xs) = insert (mySort xs) where insert [] = [x] insert (y:ys) | x <= y = x : y : ys | otherwise = y : insert ys myIsSorted :: Ord a => [a] -> Bool myIsSorted [] = True myIsSorted [_] = True myIsSorted (x:y:ls) = x <= y && myIsSorted (y : ls)

slide-26
SLIDE 26

Example code

module MySort where mySort :: Ord a => [a] -> [a] mySort [] = [] mySort (x:xs) = insert (mySort xs) where insert [] = [x] insert (y:ys) | x <= y = x : y : ys | otherwise = y : insert ys myIsSorted :: Ord a => [a] -> Bool myIsSorted [] = True myIsSorted [_] = True myIsSorted (x:y:ls) = x <= y && myIsSorted (y : ls)

slide-27
SLIDE 27

Property discovery using QuickSpec

== Signature == True :: Bool (<=) :: Ord a => a -> a -> Bool (:) :: a -> [a] -> [a] mySort :: Ord a => [a] -> [a] myIsSorted :: Ord a => [a] -> Bool

slide-28
SLIDE 28

Property discovery using QuickSpec

== Signature == True :: Bool (<=) :: Ord a => a -> a -> Bool (:) :: a -> [a] -> [a] mySort :: Ord a => [a] -> [a] myIsSorted :: Ord a => [a] -> Bool == Laws ==

  • 1. y <= y = True
  • 2. y <= True = True
  • 3. True <= x = x
  • 4. myIsSorted (mySort xs) = True
  • 5. mySort (mySort xs) = mySort xs
  • 6. xs <= mySort xs = myIsSorted xs
  • 7. mySort xs <= xs = True
  • 8. myIsSorted (y : (y : xs)) = myIsSorted (y : xs)
  • 9. mySort (y : mySort xs) = mySort (y : xs)
slide-29
SLIDE 29

Property discovery using QuickSpec

== Signature == True :: Bool (<=) :: Ord a => a -> a -> Bool (:) :: a -> [a] -> [a] mySort :: Ord a => [a] -> [a] myIsSorted :: Ord a => [a] -> Bool == Laws ==

  • 1. y <= y = True
  • 2. y <= True = True
  • 3. True <= x = x
  • 4. myIsSorted (mySort xs) = True
  • 5. mySort (mySort xs) = mySort xs
  • 6. xs <= mySort xs = myIsSorted xs
  • 7. mySort xs <= xs = True
  • 8. myIsSorted (y : (y : xs)) = myIsSorted (y : xs)
  • 9. mySort (y : mySort xs) = mySort (y : xs)
slide-30
SLIDE 30

QuickSpec Code

{-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE FlexibleContexts #-} module MySortQuickSpec where import Control.Monad import MySort import QuickSpec main :: IO () main = void $ quickSpec signature { constants = [ constant "True" (True :: Bool) , constant "<=" (mkDict (<=) :: Dict (Ord A) -> A -> A -> Bool) , constant ":" ((:) :: A -> [A] -> [A]) , constant "mySort" (mkDict mySort :: Dict (Ord A) -> [A] -> [A]) , constant "myIsSorted" (mkDict myIsSorted :: Dict (Ord A) -> [A] -> Bool) ] } mkDict :: (c => a)

  • > Dict c
  • > a

mkDict x Dict = x

slide-31
SLIDE 31

Problems with QuickSpec: Monomorphisation

Only for monomorphic functions constant "<" (mkDict (<) :: Dict (Ord A) -> A -> A -> Bool)

slide-32
SLIDE 32

Problems with QuickSpec: Code

Programmer has to write code for all functions of interest 15 lines of subject code. 33 lines of QuickSpec code.

slide-33
SLIDE 33

Problems with QuickSpec: Speed

Dumb version of the QuickSpec approach:

  • 1. Generate all possible terms
  • 2. Generate all possible equations (tuples) of terms
  • 3. Type check them to make sure the equation makes sense
  • 4. Check that the input can be generated and the output

compared for equality

  • 5. Run QuickCheck to see if the equation holds
slide-34
SLIDE 34

Pause slide with a joke

strictId :: a -> a strictId !x = x

slide-35
SLIDE 35

Property Discovery with EasySpec

slide-36
SLIDE 36

Step 1: Automation

slide-37
SLIDE 37

Signatures

{-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE FlexibleContexts #-} module MySortQuickSpec where import Control.Monad import MySort import QuickSpec main :: IO () main = void $ quickSpec signature { constants = [ constant "True" (True :: Bool) , constant "<=" (mkDict (<=) :: Dict (Ord A) -> A -> A -> Bool) , constant ":" ((:) :: A -> [A] -> [A]) , constant "mySort" (mkDict mySort :: Dict (Ord A) -> [A] -> [A]) , constant "myIsSorted" (mkDict myIsSorted :: Dict (Ord A) -> [A] -> Bool) ] } mkDict :: (c => a)

  • > Dict c
  • > a

mkDict x Dict = x

slide-38
SLIDE 38

Signatures

{-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE FlexibleContexts #-} module MySortQuickSpec where import Control.Monad import MySort import QuickSpec main :: IO () main = void $ quickSpec signature { constants = [ constant "True" (True :: Bool) , constant "<=" (mkDict (<=) :: Dict (Ord A) -> A -> A -> Bool) , constant ":" ((:) :: A -> [A] -> [A]) , constant "mySort" (mkDict mySort :: Dict (Ord A) -> [A] -> [A]) , constant "myIsSorted" (mkDict myIsSorted :: Dict (Ord A) -> [A] -> Bool) ] } mkDict :: (c => a)

  • > Dict c
  • > a

mkDict x Dict = x

slide-39
SLIDE 39

A QuickSpec Signature

data Signature = Signature { constants :: [Constant], instances :: [[Instance]], [...] background :: [Prop], [...] } quickSpec :: Signature -> IO Signature

slide-40
SLIDE 40

Automatic Monomorphisation

filter :: (a -> Bool) -> [a] -> [a] becomes filter :: (A -> Bool) -> [A] -> [A]

slide-41
SLIDE 41

Automatic Monomorphisation

filter :: (a -> Bool) -> [a] -> [a] becomes filter :: (A -> Bool) -> [A] -> [A] sort :: Ord a => [a] -> [a] becomes sort :: Dict (Ord A) -> [A] -> [A]

slide-42
SLIDE 42

Signature Expression Generation

slide-43
SLIDE 43

Signature Expression Generation

sort :: Ord a => [a] -> [a]

slide-44
SLIDE 44

Signature Expression Generation

sort :: Ord a => [a] -> [a] sort :: Dict (Ord A) => [A] -> [A]

slide-45
SLIDE 45

Signature Expression Generation

sort :: Ord a => [a] -> [a] sort :: Dict (Ord A) => [A] -> [A] constant "sort" (mkDict sort :: Dict (Ord A) -> [A] -> [A])

slide-46
SLIDE 46

Signature Expression Generation

sort :: Ord a => [a] -> [a] sort :: Dict (Ord A) => [A] -> [A] constant "sort" (mkDict sort :: Dict (Ord A) -> [A] -> [A]) signature { constants = [...] }

slide-47
SLIDE 47

Current situation

$ cat Reverse.hs {-# LANGUAGE NoImplicitPrelude #-} module Reverse where import Data.List (reverse, sort)

slide-48
SLIDE 48

Current situation

$ cat Reverse.hs {-# LANGUAGE NoImplicitPrelude #-} module Reverse where import Data.List (reverse, sort) $ easyspec discover Reverse.hs reverse (reverse xs) = xs sort (reverse xs) = sort xs

slide-49
SLIDE 49

Pause slide with a joke

safePerformIO :: IO a -> IO a safePerformIO ioa = ioa >>= return

slide-50
SLIDE 50

Automated, but still slow

1 10 100 5 10 15

scope−size (functions) log(runtime) (seconds)

slide-51
SLIDE 51

Definitions

slide-52
SLIDE 52

Definitions: Property

Example: reverse (reverse ls) = ls Short for: (\ls -> reverse (reverse ls)) = (\ls -> ls) In general: (f :: A -> B) = (g :: A -> B) for some A and B with instance Arbitrary A instance Eq B

slide-53
SLIDE 53

Definitions: Size of property

Example: xs <= mySort xs = myIsSorted xs

slide-54
SLIDE 54

Definitions: Size of property

Example: xs <= mySort xs = myIsSorted xs Size: 4

slide-55
SLIDE 55

Definitions: Size of property

Example: xs <= mySort xs = myIsSorted xs Size: 4 In general: It’s complicated

slide-56
SLIDE 56

Definitions: Property of a function

Functions: f = (* 2) g = (* 3) z = 0 Properties of f: f (g x) = g (f x) f z = z Not properties of f: g z = z

slide-57
SLIDE 57

Definitions: Relevant function

Functions: f = (* 2) g = (* 3) z = 0 h = id Properties: f (g x) = g (f x) f z = z g z = z h x = x g and z are relevant to f but h is not. relevant property = property of focus function

slide-58
SLIDE 58

Definitions: Scope

Scope: Functions in scope

slide-59
SLIDE 59

Definitions: Scope

Scope: Functions in scope Size of scope: Number of functions in scope

slide-60
SLIDE 60

Definitions: Scope

Scope: Functions in scope Size of scope: Number of functions in scope Size of signature: Number of functions in signature

slide-61
SLIDE 61

Automated, but still slow

1 10 100 5 10 15

scope−size (functions) log(runtime) (seconds)

slide-62
SLIDE 62

Why is this slow?

  • 1. Maximum size of the discovered properties
slide-63
SLIDE 63

Why is this slow?

  • 1. Maximum size of the discovered properties
  • 2. Size of the signature
slide-64
SLIDE 64

Idea

slide-65
SLIDE 65

Critical insight

We are not interested in the entire codebase. We are interested in a relatively small amount of code.

slide-66
SLIDE 66

Reducing the size of the signature

inferSignature :: [Function] -- Focus functions

  • > [Function] -- Functions in scope
  • > [Function] -- Chosen functions
slide-67
SLIDE 67

Full background and empty background

inferFullBackground _ scope = scope inferEmptyBackground focus _ = focus

slide-68
SLIDE 68

Full background and empty background

inferFullBackground _ scope = scope inferEmptyBackground focus _ = focus

100 200 300 5 10 15

scope−size ( # functions ) runtime ( time seconds ) strategy

empty−background full−background

slide-69
SLIDE 69

Full background and empty background

inferFullBackground _ scope = scope inferEmptyBackground focus _ = focus

  • empty−background

full−background 5 10 15 20 25 30 35

Boxplot for relevant−equations (More is better.)

relevant−equations ( # equations )

slide-70
SLIDE 70

Pause slide with a joke

safeCoerce :: a ~ b => a -> b safeCoerce x = x

slide-71
SLIDE 71

Syntactic similarity: Name

inferSyntacticSimilarityName [focus] scope = take 5 $ sortOn (\sf -> hammingDistance (name focus) (name sf)) scope

slide-72
SLIDE 72

Syntactic similarity: Name

inferSyntacticSimilarityName [focus] scope = take 5 $ sortOn (\sf -> hammingDistance (name focus) (name sf)) scope

100 200 300 5 10 15

scope−size ( # functions ) runtime ( time seconds ) strategy

full−background syntactical−similarity−name−5

slide-73
SLIDE 73

Syntactic similarity: Name

inferSyntacticSimilarityName [focus] scope = take 5 $ sortOn (\sf -> hammingDistance (name focus) (name sf)) scope

full−background syntactical−similarity−name−5 10 20 30 40

Boxplot for relevant−equations (More is better.)

relevant−equations ( # equations )

slide-74
SLIDE 74

Syntactic similarity: Implementation

inferSyntacticSimilaritySymbols i [focus] scope = take i $ sortOn (\sf -> hammingDistance (symbols focus) (symbols sf)) scope

slide-75
SLIDE 75

Syntactic similarity: Implementation

inferSyntacticSimilaritySymbols i [focus] scope = take i $ sortOn (\sf -> hammingDistance (symbols focus) (symbols sf)) scope

100 200 300 5 10 15

scope−size ( # functions ) runtime ( time seconds ) strategy

full−background syntactical−similarity−symbols−5

slide-76
SLIDE 76

Syntactic similarity: Implementation

inferSyntacticSimilaritySymbols i [focus] scope = take i $ sortOn (\sf -> hammingDistance (symbols focus) (symbols sf)) scope

  • full−background

syntactical−similarity−symbols−5 10 20 30

Boxplot for relevant−equations (More is better.)

relevant−equations ( # equations )

slide-77
SLIDE 77

Syntactic similarity: Type

inferSyntacticSimilarityType i [focus] scope = take i $ sortOn (\sf -> hammingDistance (getTypeParts focus) (getTypeParts sf)) scope

slide-78
SLIDE 78

Syntactic similarity: Type

inferSyntacticSimilarityType i [focus] scope = take i $ sortOn (\sf -> hammingDistance (getTypeParts focus) (getTypeParts sf)) scope

100 200 300 5 10 15

scope−size ( # functions ) runtime ( time seconds ) strategy

full−background syntactical−similarity−type−5

slide-79
SLIDE 79

Syntactic similarity: Type

inferSyntacticSimilarityType i [focus] scope = take i $ sortOn (\sf -> hammingDistance (getTypeParts focus) (getTypeParts sf)) scope

  • full−background

syntactical−similarity−type−5 10 20 30 40

Boxplot for relevant−equations (More is better.)

relevant−equations ( # equations )

slide-80
SLIDE 80

Other things we tried

  • 1. Similarity using a different metric: edit distance
  • 2. Unions of the previous strategies
slide-81
SLIDE 81

Breakthrough

Histogram of the number of different functions in an equation

Different functions relative # of cases 1 2 3 4 5 0.0 0.1 0.2 0.3 0.4

slide-82
SLIDE 82

Idea

slide-83
SLIDE 83

We can run QuickSpec more than

  • nce!
slide-84
SLIDE 84

Inferred Signature

type SignatureInferenceStrategy = [Function] -> [Function] -> InferredSignature

slide-85
SLIDE 85

Inferred Signature

type SignatureInferenceStrategy = [Function] -> [Function] -> InferredSignature Combine the results of multiple runs: type InferredSignature = [Signature]

slide-86
SLIDE 86

Inferred Signature

type SignatureInferenceStrategy = [Function] -> [Function] -> InferredSignature Combine the results of multiple runs: type InferredSignature = [Signature] User previous results as background properties: type InferredSignature = Forest Signature

slide-87
SLIDE 87

Inferred Signature

type SignatureInferenceStrategy = [Function] -> [Function] -> InferredSignature Combine the results of multiple runs: type InferredSignature = [Signature] User previous results as background properties: type InferredSignature = Forest Signature Share previous runs: type InferredSignature = DAG Signature

slide-88
SLIDE 88

Chunks

chunks :: SignatureInferenceStrategy

> chunks > [sort :: Ord a => [a] -> [a]] > [reverse :: [a] -> [a], id :: a -> a] [sort, reverse] | v

  • > [sort]

| | [sort, id]

slide-89
SLIDE 89

The runtime of chunks

100 200 300 5 10 15

scope−size ( # functions ) runtime ( time seconds ) strategy

chunks full−background

slide-90
SLIDE 90

The outcome of chunks: Relevant equations

chunks full−background 10 20 30 40 50 60

Boxplot for relevant−equations (More is better.)

relevant−equations ( # equations )

slide-91
SLIDE 91

Why does chunks find more relevant equations?

chunks full−background 20 40 60 80

Boxplot for equations (More is better.)

equations ( # equations )

slide-92
SLIDE 92

Why does chunks find more relevant equations?

Scope: i = (+ 1) j = (+ 2) k = (+ 3) l = (+ 4) m = (+ 5) n = (+ 6)

  • = (+ 7)

p = (+ 8) q = (+ 9) r = (+ 10)

slide-93
SLIDE 93

Why does chunks find more relevant equations?

Scope: i = (+ 1) j = (+ 2) k = (+ 3) l = (+ 4) m = (+ 5) n = (+ 6)

  • = (+ 7)

p = (+ 8) q = (+ 9) r = (+ 10) Full background: i (i x) = j x i (j x) = k x i (k x) = l x i (l x) = m x i (m x) = n x i (n x) = o x i (o x) = p x i (p x) = q x i (q x) = r x Relevant to r: i (q x) = r x

slide-94
SLIDE 94

Why does chunks find more relevant equations?

Scope: i = (+ 1) j = (+ 2) k = (+ 3) l = (+ 4) m = (+ 5) n = (+ 6)

  • = (+ 7)

p = (+ 8) q = (+ 9) r = (+ 10) Full background: i (i x) = j x i (j x) = k x i (k x) = l x i (l x) = m x i (m x) = n x i (n x) = o x i (o x) = p x i (p x) = q x i (q x) = r x Relevant to r: i (q x) = r x Chunks for r: q (i x) = r x q (q x) = p (r x) q (q (q x)) = o (r (r x)) q (q (q (q (q x)))) = m (r (r (r (r x)))) q (q (q (q (q (q x))))) = l (r (r (r (r (r x))))) All relevant

slide-95
SLIDE 95

Inferred Signature

type SignatureInferenceStrategy = [Function] -> [Function] -> InferredSignature type InferredSignature = DAG ([(Signature, [Equation])] -> Signature)

slide-96
SLIDE 96

Inferred Signature

type SignatureInferenceStrategy = [Function] -> [Function] -> InferM () data InferM a where InferPure :: a -> InferM a InferFmap :: (a -> b) -> InferM a -> InferM b InferApp :: InferM (a -> b) -> InferM a -> InferM b InferBind :: InferM a -> (a -> InferM b) -> InferM b InferFrom :: [EasyNamedExp]

  • > [OptiToken]
  • > InferM (OptiToken, [EasyEq])
slide-97
SLIDE 97

Chunks Plus

chunksPlus :: SignatureInferenceStrategy

> chunksPlus > [sort :: Ord a => [a] -> [a]] > [reverse :: [a] -> [a], id :: a -> a]

  • >

[sort, reverse] / | / v [sort, reverse, id]

  • > [sort]

\ | \ |

  • >

[sort, id]

slide-98
SLIDE 98

The runtime of chunks plus

100 200 300 5 10 15

scope−size ( # functions ) runtime ( time seconds ) strategy

chunks−plus full−background

slide-99
SLIDE 99

The outcome of chunks plus: Relevant equations

  • chunks−plus

full−background 20 40 60 80 100 120

Boxplot for relevant−equations (More is better.)

relevant−equations ( # equations )

slide-100
SLIDE 100

All strategies

  • chunks

chunks−plus empty−background full−background syntactical−similarity−name−5 syntactical−similarity−symbols−5 syntactical−similarity−type−5 20 40 60 80 100 120

Boxplot for relevant−equations (More is better.)

relevant−equations ( # equations )

slide-101
SLIDE 101

All strategies

100 200 300 5 10 15

scope−size runtime strategy.x

chunks chunks−plus empty−background full−background syntactical−similarity−name−5 syntactical−similarity−symbols−5 syntactical−similarity−type−5

slide-102
SLIDE 102

Neat

$ time stack exec easyspec \

  • - discover MySort.hs MySort.mySort

xs <= mySort xs = myIsSorted xs mySort xs <= xs = True myIsSorted (mySort xs) = True mySort (mySort xs) = mySort xs 3.61s user 1.14s system 193% cpu 2.450 total

slide-103
SLIDE 103

Great promise, but ...

slide-104
SLIDE 104

Great promise, but ...

  • 1. Only works for functions in scope of which the type is in scope

too.

slide-105
SLIDE 105

Great promise, but ...

  • 1. Only works for functions in scope of which the type is in scope

too.

  • 2. Crashes on partial functions.
slide-106
SLIDE 106

Great promise, but ...

  • 1. Only works for functions in scope of which the type is in scope

too.

  • 2. Crashes on partial functions.
  • 3. Only works with built in instances.
slide-107
SLIDE 107

Great promise, but ...

  • 1. Only works for functions in scope of which the type is in scope

too.

  • 2. Crashes on partial functions.
  • 3. Only works with built in instances.
  • 4. Data has to have an Arbitrary instance in scope.
slide-108
SLIDE 108

Great promise, but ...

  • 1. Only works for functions in scope of which the type is in scope

too.

  • 2. Crashes on partial functions.
  • 3. Only works with built in instances.
  • 4. Data has to have an Arbitrary instance in scope.
  • 5. Does not play with CPP.
slide-109
SLIDE 109

Great promise, but ...

  • 1. Only works for functions in scope of which the type is in scope

too.

  • 2. Crashes on partial functions.
  • 3. Only works with built in instances.
  • 4. Data has to have an Arbitrary instance in scope.
  • 5. Does not play with CPP.
  • 6. Does not play well with higher kinded type variables
slide-110
SLIDE 110

Great promise, but ...

  • 1. Only works for functions in scope of which the type is in scope

too.

  • 2. Crashes on partial functions.
  • 3. Only works with built in instances.
  • 4. Data has to have an Arbitrary instance in scope.
  • 5. Does not play with CPP.
  • 6. Does not play well with higher kinded type variables

All technical problems, not theoretical problems!

slide-111
SLIDE 111

Further Research

  • 1. Can we go faster?
slide-112
SLIDE 112

Further Research

  • 1. Can we go faster?
  • 2. Which constants do we choose for built in types?
slide-113
SLIDE 113

Further Research

  • 1. Can we go faster?
  • 2. Which constants do we choose for built in types?
  • 3. Can we apply this to effectful code?
slide-114
SLIDE 114

Further Research

  • 1. Can we go faster?
  • 2. Which constants do we choose for built in types?
  • 3. Can we apply this to effectful code?
  • 4. Relative importance of equations
slide-115
SLIDE 115

Call to action

Proofs of concept: https://github.com/nick8325/quickcheck https://github.com/nick8325/quickspec https://github.com/NorfairKing/easyspec Now we need to make it production ready!

slide-116
SLIDE 116

About Me

Student at ETH This is my master thesis Wrote Haskell in open source Taught Haskell at ETH Wrote Haskell in industry Looking for a job! https://cs-syd.eu/ https://cs-syd.eu/cv https://github.com/NorfairKing