Deriving Combinator Implementations Lecture 4, Designing and Using Combinators John Hughes.

Slides:



Advertisements
Similar presentations
Modern Programming Languages, 2nd ed.
Advertisements

ML Datatypes.1 Standard ML Data types. ML Datatypes.2 Concrete Datatypes  The datatype declaration creates new types  These are concrete data types,
Introducing Formal Methods, Module 1, Version 1.1, Oct., Formal Specification and Analytical Verification L 5.
11/10/04 AIPP Lecture 6: Built-in Predicates1 Combining Lists & Built-in Predicates Artificial Intelligence Programming in Prolog Lecturer: Tim Smith Lecture.
Kathleen Fisher cs242 Reading: “A history of Haskell: Being lazy with class”,A history of Haskell: Being lazy with class Section 6.4 and Section 7 “Monads.
Michael Alves, Patrick Dugan, Robert Daniels, Carlos Vicuna
© M. Winter COSC 4P41 – Functional Programming Testing vs Proving Testing –uses a set of “typical” examples, –symbolic testing, –may find errors,
Getting started with ML ML is a functional programming language. ML is statically typed: The types of literals, values, expressions and functions in a.
1 Discrete Structures & Algorithms Graphs and Trees: III EECE 320.
Domain Specific Embedded Languages Lecture 2, Designing and Using Combinators John Hughes.
What is a Parser? A parser is a program that analyses a piece of text to determine its syntactic structure  3 means 23+4.
A Semantic Characterization of Unbounded-Nondeterministic Abstract State Machines Andreas Glausch and Wolfgang Reisig 1.
Introduction to Computability Theory
Constraint Logic Programming Ryan Kinworthy. Overview Introduction Logic Programming LP as a constraint programming language Constraint Logic Programming.
Tirgul 10 Rehearsal about Universal Hashing Solving two problems from theoretical exercises: –T2 q. 1 –T3 q. 2.
0 PROGRAMMING IN HASKELL Chapter 7 - Higher-Order Functions.
Higher-Order Functions Koen Lindström Claessen. What is a “Higher Order” Function? A function which takes another function as a parameter. Examples map.
Katz Formal Specifications Larch 1 Algebraic Specification and Larch Formal Specifications of Complex Systems Shmuel Katz The Technion.
A Lightning Tour of Haskell Lecture 1, Designing and Using Combinators John Hughes.
0 PROGRAMMING IN HASKELL Chapter 4 - Defining Functions.
Introducing Monads Lecture 3, Designing and Using Combinators John Hughes.
Normal forms for Context-Free Grammars
Recursion Chapter 7. Chapter 7: Recursion2 Chapter Objectives To understand how to think recursively To learn how to trace a recursive method To learn.
© 2005 by Prentice Hall Chapter 9 Structuring System Requirements: Logic Modeling Modern Systems Analysis and Design Fourth Edition.
CS510AP Quick Check Monads. QuickCheck Quick check is a Haskell library for doing random testing. You can read more about quickcheck at –
CS 2104 : Prog. Lang. Concepts. Functional Programming I Lecturer : Dr. Abhik Roychoudhury School of Computing From Dr. Khoo Siau Cheng’s lecture notes.
1 Introduction to Parsing Lecture 5. 2 Outline Regular languages revisited Parser overview Context-free grammars (CFG’s) Derivations.
Formal Models of Computation Part II The Logic Model
Computing Science 1P Large Group Tutorial 17 Simon Gay Department of Computing Science University of Glasgow 2006/07.
Erlang/QuickCheck Thomas Arts, IT University John Hughes, Chalmers University Gothenburg.
CS 321 Programming Languages and Compilers Prolog part 2.
PrasadCS7761 Haskell Data Types/ADT/Modules Type/Class Hierarchy Lazy Functional Language.
Recursion Chapter 7. Chapter Objectives  To understand how to think recursively  To learn how to trace a recursive method  To learn how to write recursive.
ML Datatypes.1 Standard ML Data types. ML Datatypes.2 Concrete Datatypes  The datatype declaration creates new types  These are concrete data types,
Chapter 13 Recursion. Learning Objectives Recursive void Functions – Tracing recursive calls – Infinite recursion, overflows Recursive Functions that.
30/09/04 AIPP Lecture 3: Recursion, Structures, and Lists1 Recursion, Structures, and Lists Artificial Intelligence Programming in Prolog Lecturer: Tim.
CSC 211 Data Structures Lecture 13
0 PROGRAMMING IN HASKELL Chapter 9 - Higher-Order Functions, Functional Parsers.
Advanced Functional Programming Tim Sheard 1 Lecture 6 Functional Programming Tim Sheard & Mark Jones Monads & Interpreters.
A Second Look At ML 1. Outline Patterns Local variable definitions A sorting example 2.
Review: Compiler Phases: Source program Lexical analyzer Syntax analyzer Semantic analyzer Intermediate code generator Code optimizer Code generator Symbol.
CSE Winter 2008 Introduction to Program Verification January 31 proofs through simplification.
FUNCTIONS Relation – a set of ( x, y ) points Function – a set of ( x, y ) points where there is only one output for each specific input – x can not be.
1 CS 457/557: Functional Languages Lists and Algebraic Datatypes Mark P Jones Portland State University.
Closure Properties Lemma: Let A 1 and A 2 be two CF languages, then the union A 1  A 2 is context free as well. Proof: Assume that the two grammars are.
Top-down Parsing lecture slides from C OMP 412 Rice University Houston, Texas, Fall 2001.
Lee CSCE 314 TAMU 1 CSCE 314 Programming Languages Interactive Programs: I/O and Monads Dr. Hyunyoung Lee.
Basic Scheme February 8, 2007 Compound expressions Rules of evaluation Creating procedures by capturing common patterns.
Top-down Parsing. 2 Parsing Techniques Top-down parsers (LL(1), recursive descent) Start at the root of the parse tree and grow toward leaves Pick a production.
1/33 Basic Scheme February 8, 2007 Compound expressions Rules of evaluation Creating procedures by capturing common patterns.
Fall 2008Programming Development Techniques 1 Topic 17 Assignment, Local State, and the Environment Model of Evaluation Section 3.1 & 3.2.
Chapter SevenModern Programming Languages1 A Second Look At ML.
© 2005 by Prentice Hall Chapter 9 Structuring System Requirements: Logic Modeling Modern Systems Analysis and Design Fourth Edition Jeffrey A. Hoffer Joey.
0 PROGRAMMING IN HASKELL Based on lecture notes by Graham Hutton The book “Learn You a Haskell for Great Good” (and a few other sources) Odds and Ends,
Copyright © 2014 Curt Hill Algorithms From the Mathematical Perspective.
Basic Scheme February 8, 2007 Compound expressions Rules of evaluation
Containers and Lists CIS 40 – Introduction to Programming in Python
A lightening tour in 45 minutes
Higher-Order Functions
PROGRAMMING IN HASKELL
Higher-Order Procedures
PROGRAMMING IN HASKELL
Chapter 9 Structuring System Requirements: Logic Modeling
PROGRAMMING IN HASKELL
CSCE 314: Programming Languages Dr. Dylan Shell
Chapter 9 Structuring System Requirements: Logic Modeling
Programming Languages
Compilers Principles, Techniques, & Tools Taught by Jing Zhang
Introduction to the Lab
PROGRAMMING IN HASKELL
Presentation transcript:

Deriving Combinator Implementations Lecture 4, Designing and Using Combinators John Hughes

Can We Derive Combinators from Specifications? What sort of specifications, what sort of derivations? Equational reasoning is convenient with functional programs. Equational specifications (algebraic specifications) are directly useful for equational reasoning. We work from equational specifications.

Example: Specifying Lists We specify an abstract sequence type (lists in disguise), with operations nil :: Seq a unit :: a -> Seq a cat :: Seq a -> Seq a -> Seq a list :: Seq a -> [a] List is an observer; any abstract data type must have observations (otherwise we can represent it by ()).

Axiomatising Lists We take the following equational axioms: nil `cat` xs = xs = xs `cat` nil (xs`cat`ys)`cat`zs = xs`cat`(ys`cat`zs) list nil = [] list (unit x`cat`xs) = x : list xs These axioms are complete, in the sense that they define the value of every ground observation.

Strategy 1: A Term Implementation Represent sequences by the syntax of Seq terms: Seq := nil | unit E | Seq `cat` Seq data Seq a = Nil | Unit a | Seq a `Cat` Seq a Use the laws to derive an interpreter: list (Unit x)

Strategy 1: A Term Implementation Represent sequences by the syntax of Seq terms: Seq := nil | unit E | Seq `cat` Seq data Seq a = Nil | Unit a | Seq a `Cat` Seq a Use the laws to derive an interpreter: list (Unit x)= list (unit x)

Strategy 1: A Term Implementation Represent sequences by the syntax of Seq terms: Seq := nil | unit E | Seq `cat` Seq data Seq a = Nil | Unit a | Seq a `Cat` Seq a Use the laws to derive an interpreter: list (Unit x)= list (unit x) = list (unit x `cat` empty)

Strategy 1: A Term Implementation Represent sequences by the syntax of Seq terms: Seq := nil | unit E | Seq `cat` Seq data Seq a = Nil | Unit a | Seq a `Cat` Seq a Use the laws to derive an interpreter: list (Unit x)= list (unit x) = list (unit x `cat` empty) = x : list empty

Strategy 1: A Term Implementation Represent sequences by the syntax of Seq terms: Seq := nil | unit E | Seq `cat` Seq data Seq a = Nil | Unit a | Seq a `Cat` Seq a Use the laws to derive an interpreter: list (Unit x)= list (unit x) = list (unit x `cat` empty) = x : list empty = x : []

Strategy 1: A Term Implementation Represent sequences by the syntax of Seq terms: Seq := nil | unit E | Seq `cat` Seq data Seq a = Nil | Unit a | Seq a `Cat` Seq a Use the laws to derive an interpreter: list (Unit x)= x : [] list (xs `Cat` ys)

Strategy 1: A Term Implementation Represent sequences by the syntax of Seq terms: Seq := nil | unit E | Seq `cat` Seq data Seq a = Nil | Unit a | Seq a `Cat` Seq a Use the laws to derive an interpreter: list (Unit x)= x : [] list (xs `Cat` ys)= list (xs `cat` ys)

Strategy 1: A Term Implementation Represent sequences by the syntax of Seq terms: Seq := nil | unit E | Seq `cat` Seq data Seq a = Nil | Unit a | Seq a `Cat` Seq a Use the laws to derive an interpreter: list (Unit x)= x : [] list ((xs `Cat` ys) `Cat` zs) = list ((xs `cat` ys) `cat` zs)

Strategy 1: A Term Implementation Represent sequences by the syntax of Seq terms: Seq := nil | unit E | Seq `cat` Seq data Seq a = Nil | Unit a | Seq a `Cat` Seq a Use the laws to derive an interpreter: list (Unit x)= x : [] list ((xs `Cat` ys) `Cat` zs) = list ((xs `cat` ys) `cat` zs) = list (xs `cat` (ys `cat` zs))

Strategy 1: A Term Implementation Represent sequences by the syntax of Seq terms: Seq := nil | unit E | Seq `cat` Seq data Seq a = Nil | Unit a | Seq a `Cat` Seq a Use the laws to derive an interpreter: list (Unit x)= x : [] list ((xs `Cat` ys) `Cat` zs) = list ((xs `cat` ys) `cat` zs) = list (xs `cat` (ys `cat` zs)) = list (xs `Cat` (ys `Cat` zs))

Strategy 1: A Term Implementation list Nil = [] list (Unit x) = [x] list (Nil `Cat` xs) = list xs list (Unit x `Cat` xs) = x : list xs list ((xs `Cat` ys) `Cat` zs) = list (xs `Cat` (ys `Cat` zs)) The complete interpreter is: But we can do better…

Strategy 2: A Simplified Term Implementation The laws can be used to simplify terms. Claim: Every Seq term can be simplified to the form: Sseq ::= nil | unit E `cat` Sseq data Seq a = Nil | a `UnitCat` (Seq a)

Strategy 2: A Simplified Term Implementation Sseq ::= nil | unit E `cat` Sseq data Seq a = Nil | a `UnitCat` (Seq a) (x `UnitCat` xs) `cat` ys Operations must convert simplified arguments to simplified results.

Strategy 2: A Simplified Term Implementation Sseq ::= nil | unit E `cat` Sseq data Seq a = Nil | a `UnitCat` (Seq a) (x `UnitCat` xs) `cat` ys= (unit x `cat` xs) `cat` ys Operations must convert simplified arguments to simplified results.

Strategy 2: A Simplified Term Implementation Sseq ::= nil | unit E `cat` Sseq data Seq a = Nil | a `UnitCat` (Seq a) (x `UnitCat` xs) `cat` ys= (unit x `cat` xs) `cat` ys = unit x `cat` (xs `cat` ys) Operations must convert simplified arguments to simplified results.

Strategy 2: A Simplified Term Implementation Sseq ::= nil | unit E `cat` Sseq data Seq a = Nil | a `UnitCat` (Seq a) (x `UnitCat` xs) `cat` ys= (unit x `cat` xs) `cat` ys = unit x `cat` (xs `cat` ys) = x `UnitCat` (xs `cat` ys) Operations must convert simplified arguments to simplified results.

Strategy 2: A Simplified Term Implementation The complete derived definitions are: nil = Nil unit x = x `UnitCat` Nil Nil `cat` xs = xs (x `UnitCat xs) `cat` ys = x `UnitCat` (xs `cat` ys) list nil = [] list (x `UnitCat` xs) = x : list xs

Strategy 2: A Simplified Term Implementation The complete derived definitions are: nil = Nil unit x = x `UnitCat` Nil Nil `cat` xs = xs (x `UnitCat xs) `cat` ys = x `UnitCat` (xs `cat` ys) list nil = [] list (x `UnitCat` xs) = x : list xs These are true equations, we must prove separately they are a terminating definition.

Strategy 2: A Simplified Term Implementation The complete derived definitions are: nil = Nil unit x = x `UnitCat` Nil Nil `cat` xs = xs (x `UnitCat xs) `cat` ys = x `UnitCat` (xs `cat` ys) list nil = [] list (x `UnitCat` xs) = x : list xs These are true equations, we must prove separately they are a terminating definition. A constructive proof that all terms can be expressed in the simplified form!

Strategy 2: A Simplified Term Implementation The complete derived definitions are: nil = Nil unit x = x `UnitCat` Nil Nil `cat` xs = xs (x `UnitCat xs) `cat` ys = x `UnitCat` (xs `cat` ys) list nil = [] list (x `UnitCat` xs) = x : list xs These are true equations, we must prove separately they are a terminating definition. A constructive proof that all terms can be expressed in the simplified form! Just lists in disguise: UnitCat = (:), Nil = []

A Problem Evaluating ((unit x1 `cat` unit x2) `cat` unit x3) … `cat` unit xn is quadratic! Associativity converts this to unit x1 `cat` (unit x2 `cat` (unit x3 … `cat` unit xn)) whose evaluation is linear. But `cat` cannot apply associativity… (it doesn’t “see” the inner call of cat).

Strategy 3: Context Passing Implementation Idea: Pass `cat` a representation of its context. (xs `cat` ys) `cat` zs Inner `cat` recognises it’s the left arg of a `cat`

Strategy 3: Context Passing Implementation Idea: Pass `cat` a representation of its context. (xs `cat` ys) `cat` zs Inner `cat` recognises it’s the left arg of a `cat` xs `cat` (ys `cat` zs) Rewrites by associativity to more efficient form

Interlude: What is a Context? A context is an expression “with a = list `cat` zs) We can “fill the hole” with an expression. C[xs `cat` ys] = list ((xs `cat` ys) `cat` zs)

Strategy 3: Context Passing Implementation Claim: We only need to evaluate sequences in contexts of the form list `cat` zs) Idea: represent contexts as values newtype Cxt a = ListCat (Seq a) Represent sequences as functions type Seq a = Cxt a -> [a] Type of the observation

Strategy 3: Context Passing Implementation ListCat zs = list `cat` zs) Let’s derive some cases: nil (ListCat zs)

Strategy 3: Context Passing Implementation ListCat zs = list `cat` zs) Let’s derive some cases: nil (ListCat zs)= list `cat` zs)

Strategy 3: Context Passing Implementation ListCat zs = list `cat` zs) Let’s derive some cases: nil (ListCat zs)= list (nil `cat` zs) = list zs

Strategy 3: Context Passing Implementation ListCat zs = list `cat` zs) Let’s derive some cases: nil (ListCat zs)= list zs unit x (ListCat zs)

Strategy 3: Context Passing Implementation ListCat zs = list `cat` zs) Let’s derive some cases: nil (ListCat zs)= list zs unit x (ListCat zs)= list (unit x `cat` zs)

Strategy 3: Context Passing Implementation ListCat zs = list `cat` zs) Let’s derive some cases: nil (ListCat zs)= list zs unit x (ListCat zs)= list (unit x `cat` zs) = x : list zs

Strategy 3: Context Passing Implementation ListCat zs = list `cat` zs) Let’s derive some cases: nil (ListCat zs)= list zs unit x (ListCat zs)= x : list zs We always seem to need list zs Why not store list zs rather than zs? Change variables!

Strategy 3: Context Passing Implementation ListCat (list zs) = list `cat` zs) Let’s derive some cases: nil (ListCat zs)= zs unit x (ListCat zs)= x : zs Replace list zs by zs

Strategy 3: Context Passing Implementation ListCat (list zs) = list `cat` zs) Let’s derive some cases: nil (ListCat zs)= zs unit x (ListCat zs)= x : zs (xs `cat` ys) (ListCat zs) Replace list zs by zs

Strategy 3: Context Passing Implementation ListCat (list zs) = list `cat` zs) Let’s derive some cases: nil (ListCat zs)= zs unit x (ListCat zs)= x : zs (xs `cat` ys) (ListCat zs) = list ((xs `cat` ys) `cat` zs) Replace list zs by zs This is the original zs -- we must eliminate it!

Strategy 3: Context Passing Implementation ListCat (list zs) = list `cat` zs) Let’s derive some cases: nil (ListCat zs)= zs unit x (ListCat zs)= x : zs (xs `cat` ys) (ListCat zs) = list ((xs `cat` ys) `cat` zs) = list (xs `cat` (ys `cat` zs)) Replace list zs by zs

Strategy 3: Context Passing Implementation ListCat (list zs) = list `cat` zs) Let’s derive some cases: nil (ListCat zs)= zs unit x (ListCat zs)= x : zs (xs `cat` ys) (ListCat zs) = list ((xs `cat` ys) `cat` zs) = list (xs `cat` (ys `cat` zs)) = xs (ListCat (list (ys `cat` zs))) Replace list zs by zs

Strategy 3: Context Passing Implementation ListCat (list zs) = list `cat` zs) Let’s derive some cases: nil (ListCat zs)= zs unit x (ListCat zs)= x : zs (xs `cat` ys) (ListCat zs) = list ((xs `cat` ys) `cat` zs) = list (xs `cat` (ys `cat` zs)) = xs (ListCat (list (ys `cat` zs))) = xs (ListCat (ys (ListCat (list zs)))) Replace list zs by zs

Strategy 3: Context Passing Implementation ListCat (list zs) = list `cat` zs) Let’s derive some cases: nil (ListCat zs)= zs unit x (ListCat zs)= x : zs (xs `cat` ys) (ListCat zs) = list ((xs `cat` ys) `cat` zs) = list (xs `cat` (ys `cat` zs)) = xs (ListCat (list (ys `cat` zs))) = xs (ListCat (ys (ListCat zs))) Replace list zs by zs zs reintroduced

Strategy 3: Context Passing Implementation ListCat (list zs) = list `cat` zs) Let’s derive some cases: nil (ListCat zs)= zs unit x (ListCat zs)= x : zs (xs `cat` ys) (ListCat zs) = xs (ListCat (ys (ListCat zs))) list xs Replace list zs by zs

Strategy 3: Context Passing Implementation ListCat (list zs) = list `cat` zs) Let’s derive some cases: nil (ListCat zs)= zs unit x (ListCat zs)= x : zs (xs `cat` ys) (ListCat zs) = xs (ListCat (ys (ListCat zs))) list xs= list (xs `cat` nil) Replace list zs by zs

Strategy 3: Context Passing Implementation ListCat (list zs) = list `cat` zs) Let’s derive some cases: nil (ListCat zs)= zs unit x (ListCat zs)= x : zs (xs `cat` ys) (ListCat zs) = xs (ListCat (ys (ListCat zs))) list xs= list (xs `cat` nil) = xs (ListCat (list nil)) Replace list zs by zs

Strategy 3: Context Passing Implementation ListCat (list zs) = list `cat` zs) Let’s derive some cases: nil (ListCat zs)= zs unit x (ListCat zs)= x : zs (xs `cat` ys) (ListCat zs) = xs (ListCat (ys (ListCat zs))) list xs= list (xs `cat` nil) = xs (ListCat (list nil)) = xs (ListCat []) Replace list zs by zs

Strategy 3: Context Passing Implementation Recall definition of Cxt: newtype Cxt a = ListCat (Seq a) A newtype is unnecessary here: we can just drop the constructor ListCat.

Strategy 3: Context Passing Implementation Collected definitions: type Seq a = [a] -> [a] nil zs = zs unit x zs = x : zs (xs `cat` ys) zs = xs (ys zs) list xs = xs [] A sequence is a function that prepends its elements to a list. Evaluation of expressions is linear time.

Summary of Strategies Strategy 1: represent terms by syntax, use laws to derive an interpreter. Strategy 2: represent terms by syntax of simplified forms, use laws to derive definitions which provide a constructive proof that every expression can be simplified. Strategy 3: represent terms by functions from context to observations, use laws to derive definitions which make context-sensitive optimisations.

So? Can we apply this to implement DSELs? YES! We show how to derive a monad transformer.

Specifying Monads All monads should satisfy the monad laws: return x >>= f = f x m >>= return = m (m >>= f) >>= g = m >>= (\x-> f x>>=g) Monad transformers should satisfy: lift (return x) = return x lift (m >>= f) = lift m >>= (\x->lift (f x))

Specifying Run run performs actions, as they are produced. run (lift m >>= f) = m >>= \x->run (f x) run (return x) = return x

Specifying Failure failure and handle form a monoid: failure `handle` m = m m `handle` failure = m (x `handle` y) `handle` z = x `handle` (y `handle` z)

Failure/Monad Interaction These are the key properties that determine how failures behave: failure >>= f = failure return x `handle` h = return x (lift a >>= f) `handle` h = lift a >>= \x->f x `handle` h Handler discarded on success Commit to actions once they cannot fail

Failure/Monad Interaction These are the key properties that determine how failures behave: failure >>= f = failure return x `handle` h = return x (lift a >>= f) `handle` h = lift a >>= \x->f x `handle` h Compare backtracking: (a `handle` b) >>= f = (a>>=f) `handle` (b>>=f) B can be chosen even if f fails later

Missing Laws Note two laws we don’t have: (a `handle` b) >>= f = ??? (a >>= f) `handle` b = ??? We cannot move handlers in or out through bind: has implications for the implementation.

How Do We Choose Simplified Forms? Guess a suitable set (small) Apply all operators, try to simplify back to the chosen forms If you fail, add new forms to the set and repeat! First stab return x failure

How Do We Choose Simplified Forms? Guess a suitable set (small) Apply all operators, try to simplify back to the chosen forms If you fail, add new forms to the set and repeat! First stab return x failure lift a has no simplified form

How Do We Choose Simplified Forms? Guess a suitable set (small) Apply all operators, try to simplify back to the chosen forms If you fail, add new forms to the set and repeat! First stab return x failure lift a lift a has no simplified form Add lift a

How Do We Choose Simplified Forms? Guess a suitable set (small) Apply all operators, try to simplify back to the chosen forms If you fail, add new forms to the set and repeat! First stab return x failure lift a lift a>>=f has no simplified form

How Do We Choose Simplified Forms? Guess a suitable set (small) Apply all operators, try to simplify back to the chosen forms If you fail, add new forms to the set and repeat! First stab return x failure lift a lift a>>=f lift a>>=f has no simplified form Add lift a>>=f

How Do We Choose Simplified Forms? Guess a suitable set (small) Apply all operators, try to simplify back to the chosen forms If you fail, add new forms to the set and repeat! First stab return x failure lift a lift a>>=f No longer needed: lift a = lift a>>=\x->return x

How Do We Choose Simplified Forms? Guess a suitable set (small) Apply all operators, try to simplify back to the chosen forms If you fail, add new forms to the set and repeat! First stab return x failure lift a>>=f A complete set! All expressions can be simplified to one of these forms.

Simplified Term Implementation return x failure lift a>>=f data Failure m a = Return a | Failure | LiftBind (m a) (a -> Failure m a) return x = Return x failure = Failure lift a = LiftBind a Return Return x>>=f = f x Failure>>=f = Failure LiftBind a f>>=g = LiftBind a (\x->f x>>=g) Return x`handle`h = Return x Failure`handle`h = h LiftBind a f`handle`h = LiftBind a (\x->f x`handle`h)

What About Context Passing? The following contexts suffice: ::= | | We choose a representation data Cxt ans a = Run (a->ans) | forall b. Bind (Cxt ans b) (a -> ans) | Handle (Cxt ans a) ans Stack of continuations and exception handlers

Summary So Far… We’ve seen strategies for deriving term-based and context- passing implementations from an algebraic specification. We’ve seen them applied to a simple ADT and a monad transformer. Now for a real application…

Prettyprinting Library Reminder: A pretty-printer for binary trees: data Tree a = Leaf | Node a (Tree a) (Tree a) prettyTree Leaf = text “Leaf” prettyTree (Node a left right) = text (“(Node ”++show a++“ ”) <> sep [prettyTree left, prettyTree right] <> text “)” (Node 2 (Node 1 Leaf Leaf) (Node 1 Leaf Leaf)) Example: outputs

Prettyprinting Operations pretty :: Doc -> String text :: String -> Docliteral string (<>), ($$) :: Doc -> Doc -> Dochorizontal and vertical composition nest :: Integer -> Doc -> Docindentation sep :: [Doc] -> Docalternative layouts sep [d1…dn] = d1 … dn or d1 $$ … $$ dn d1 d2 = d1<>text “ ”<>d2

Horizontal Composition Hello sky, hello clouds on high <> Hello sky, hello clouds on high Text always joins up -- so indentation of 2nd arg is lost a<>nest k b = a<>b nest k a<>b = nest k (a<>b)

Interesting Laws (a<>b)<>c = a<>(b<>c) (a$$b)$$c = a$$(b$$c) (a$$b)<>c = a$$(b<>c) (a<>b)$$c  a<>(b$$c) a b c aa bb cc This is what makes prettyprinting difficult!

Specialised Laws (text (s++t)<>a)$$b = text s<>((text t<>a)$$nest(-length s)b st a b length s sep [text (s++t)<>d1,…,dn] = text s<>sep[text t<>d1,nest(-k)d2…nest(-k)dn] where k = length s Reduces search: lets us generate output before exploring alternatives

Results The current implementation uses a simplified-term based representation, with many optimisations thanks to the algebra. The datatype and code is complex and impenetrable: could not be invented by informal methods. First (informal) implementation was buggy and slow: both performance and behaviour much improved by a formal approach. Extended by Simon Peyton-Jones, using same formal methods. Now used for all pretty-printers in GHC, hbc, …

Summary Algebraic specifications and equational reasoning are a good match. We have three standard strategies (term based, simplified term based, context passing) for deriving implementations from algebraic specifications. Methods are good enough to develop real libraries in widespread use.