Forward Recursion. Programming Languages and Compilers (CS 421) Mapping Recursion. Forward Recursion: Examples. Folding Recursion.

Similar documents
Programming Languages and Compilers (CS 421)

n What is its running time? 9/18/17 2 n poor_rev [1,2,3] = n (poor_rev [1] = n ((poor_rev [1] =

Programming Languages and Compilers (CS 421)

Functions Over Lists. Programming Languages and Compilers (CS 421) Structural Recursion. Functions Over Lists. Structural Recursion : List Example

Booleans (aka Truth Values) Programming Languages and Compilers (CS 421) Booleans and Short-Circuit Evaluation. Tuples as Values.

# true;; - : bool = true. # false;; - : bool = false 9/10/ // = {s (5, "hi", 3.2), c 4, a 1, b 5} 9/10/2017 4

Forward recursion. CS 321 Programming Languages. Functions calls and the stack. Functions calls and the stack

CS Lecture 6: Map and Fold. Prof. Clarkson Spring Today s music: Selections from the soundtrack to 2001: A Space Odyssey

CS 321 Programming Languages

Programming Languages and Compilers (CS 421)

FUNCTIONAL PROGRAMMING

CS Lecture 6: Map and Fold. Prof. Clarkson Fall Today s music: Selections from the soundtrack to 2001: A Space Odyssey

Map and Fold. Prof. Clarkson Fall Today s music: Selections from the soundtrack to 2001: A Space Odyssey

MP 2 Continuation-Passing Style

Programming Languages and Compilers (CS 421)

CSc 520. Gofer III. Accumulative Recursion. Accumulative Recursion... Stack Recursion. Principles of Programming Languages. Christian Collberg

CSE341: Programming Languages Lecture 6 Nested Patterns Exceptions Tail Recursion. Zach Tatlock Winter 2018

Programming Languages & Compilers. Programming Languages and Compilers (CS 421) Programming Languages & Compilers. Major Phases of a Compiler

Principles of Programming Languages Topic: Functional Programming Professor L. Thorne McCarty Spring 2003

Programming Languages and Compilers (CS 421)

School of Computer Science

List Functions, and Higher-Order Functions

Topic 5: Examples and higher-order functions

Programming Languages & Compilers. Programming Languages and Compilers (CS 421) I. Major Phases of a Compiler. Programming Languages & Compilers

Programming Languages and Compilers (CS 421)

Tail Recursion. ;; a recursive program for factorial (define fact (lambda (m) ;; m is non-negative (if (= m 0) 1 (* m (fact (- m 1))))))

n (0 1)*1 n a*b(a*) n ((01) (10))* n You tell me n Regular expressions (equivalently, regular 10/20/ /20/16 4

Programming Languages and Compilers (CS 421)

Programming Languages and Compilers (CS 421)

Recap: Functions as first-class values

n Takes abstract syntax trees as input n In simple cases could be just strings n One procedure for each syntactic category

Parsing Scheme (+ (* 2 3) 1) * 1

The Art of Recursion: Problem Set 10

Compiler Construction Lecture 05 A Simple Stack Machine. Lent Term, Lecturer: Timothy G. Griffin. Computer Laboratory University of Cambridge

Two Problems. Programming Languages and Compilers (CS 421) Type Inference - Example. Type Inference - Outline. Type Inference - Example

Functional Programming. Pure Functional Programming

CSCI-GA Scripting Languages

Continuations and Continuation-Passing Style

Compilers: The goal. Safe. e : ASM

Functional Programming. Pure Functional Programming

Closures. Mooly Sagiv. Michael Clarkson, Cornell CS 3110 Data Structures and Functional Programming

Code example: sfact SICP Explicit-control evaluator. Example register machine: instructions. Goal: a tail-recursive evaluator.

Discussion 12 The MCE (solutions)

CSE 413 Midterm, May 6, 2011 Sample Solution Page 1 of 8

Programming Languages and Compilers (CS 421)

Interpreters and Tail Calls Fall 2017 Discussion 8: November 1, 2017 Solutions. 1 Calculator. calc> (+ 2 2) 4

Resources matter. Orders of Growth of Processes. R(n)= (n 2 ) Orders of growth of processes. Partial trace for (ifact 4) Partial trace for (fact 4)

Accurate Step Counting

SNU Programming Language Theory

News. CSE 130: Programming Languages. Environments & Closures. Functions are first-class values. Recap: Functions as first-class values

CS422 - Programming Language Design

Closures. Mooly Sagiv. Michael Clarkson, Cornell CS 3110 Data Structures and Functional Programming

Continuation Passing Style. Continuation Passing Style

A brief tour of history

Introduction to OCaml

Introduction to Scheme

COSE212: Programming Languages. Lecture 4 Recursive and Higher-Order Programming

Why OCaml? Introduction to Objective Caml. Features of OCaml. Applications written in OCaml. Stefan Wehr

Introduction to Objective Caml

6.821 Jeopardy: The Home Version The game that turns into 6.82fun!

n <exp>::= 0 1 b<exp> <exp>a n <exp>m<exp> 11/7/ /7/17 4 n Read tokens left to right (L) n Create a rightmost derivation (R)

Metaprogramming assignment 3

An Explicit-Continuation Metacircular Evaluator

6.001 Notes: Section 4.1

Wellesley College CS251 Programming Languages Spring, 2000 FINAL EXAM REVIEW PROBLEM SOLUTIONS

Gain familiarity and practice with folding and mapping. The following supplementary materials may be helpful in completing this assignment:

Programming Languages and Compilers (CS 421)

Tail Calls. CMSC 330: Organization of Programming Languages. Tail Recursion. Tail Recursion (cont d) Names and Binding. Tail Recursion (cont d)

Warm-up and Memoization

CS143 - Written Assignment 4 Reference Solutions

CSE 341 : Programming Languages Midterm, Spring 2015

Konzepte von Programmiersprachen

CS 11 Ocaml track: lecture 2

SCHEME 7. 1 Introduction. 2 Primitives COMPUTER SCIENCE 61A. October 29, 2015

Lists. Michael P. Fourman. February 2, 2010

Summer 2017 Discussion 10: July 25, Introduction. 2 Primitives and Define

Lab 7: OCaml 12:00 PM, Oct 22, 2017

CMSC 330, Fall 2013, Practice Problems 3

CSCI-GA Final Exam

Factorial. Next. Factorial. How does it execute? Tail recursion. How does it execute? More on recursion. Higher-order functions

Register Machines. Connecting evaluators to low level machine code

CS 421, Fall Sample Final Questions & Solutions

CSE341 Section 3. Standard-Library Docs, First-Class Functions, & More

Compiler Theory. (Semantic Analysis and Run-Time Environments)

CS 61A Interpreters, Tail Calls, Macros, Streams, Iterators. Spring 2019 Guerrilla Section 5: April 20, Interpreters.

Next. Tail Recursion: Factorial. How does it execute? Tail recursion. Tail recursive factorial. Tail recursive factorial.

CSE 341 Lecture 5. efficiency issues; tail recursion; print Ullman ; 4.1. slides created by Marty Stepp

INTERPRETERS 8. 1 Calculator COMPUTER SCIENCE 61A. November 3, 2016

// Body of shortestlists starts here y flatmap { case Nil => Nil case x :: xs => findshortest(list(x), xs)

COP4020 Programming Languages. Functional Programming Prof. Robert van Engelen

max function Next concat function max function What s the pattern? concat function More on recursion Higher-order functions

Module 8: Local and functional abstraction

CS3110 Spring 2016 Lecture 6: Modules

Modules and Representation Invariants

Announcements. Scope, Function Calls and Storage Management. Block Structured Languages. Topics. Examples. Simplified Machine Model.

MPRI course 2-4 Functional programming languages Exercises

MoreIntro_annotated.v. MoreIntro_annotated.v. Printed by Zach Tatlock. Oct 04, 16 21:55 Page 1/10

CSCC24 Functional Programming Scheme Part 2

FOFL and FOBS: First-Order Functions

Begin at the beginning

Transcription:

Forward Recursion Programming Languages and Compilers (CS 421) Elsa L Gunter 2112 SC, UIUC http://www.cs.uiuc.edu/class/cs421/ Based in part on slides by Mattox Beckman, as updated by Vikram Adve and Gul Agha In structural recursion, you split your input into components In forward recursion, you first call the function recursively on all the recursive components, and then build the final result from the partial results Wait until the whole structure has been traversed to start building the answer 9/8/09 1 9/8/09 2 Forward Recursion: Examples # let rec double_up list = match list with [ ] -> [ ] (x :: xs) -> (x :: x :: double_up xs);; val double_up : 'a list -> 'a list = <fun> # let rec poor_rev list = match list with [] -> [] (x::xs) -> poor_rev xs @ [x];; val poor_rev : 'a list -> 'a list = <fun> Mapping Recursion One common form of structural recursion applies a function to each element in the structure # let rec doublelist list = match list with [ ] -> [ ] x::xs -> 2 * x :: doublelist xs;; val doublelist : int list -> int list = <fun> # doublelist [2;3;4];; - : int list = [4; 6; 8] 9/8/09 3 9/8/09 4 Mapping Recursion Can use the higher-order recursive map function instead of direct recursion # let doublelist list = List.map (fun x -> 2 * x) list;; val doublelist : int list -> int list = <fun> # doublelist [2;3;4];; - : int list = [4; 6; 8] Same function, but no rec Folding Recursion Another common form folds an operation over the elements of the structure # let rec multlist list = match list with [ ] -> 1 x::xs -> x * multlist xs;; val multlist : int list -> int = <fun> # multlist [2;4;6];; - : int = 48 Computes (2 * (4 * (6 * 1))) 9/8/09 5 9/8/09 6

Folding Recursion multlist folds to the right Same as: # let multlist list = List.fold_right (fun x -> fun p -> x * p) list 1;; val multlist : int list -> int = <fun> # multlist [2;4;6];; - : int = 48 How long will it take? Remember the big-o notation from CS 225 and CS 273 Question: given input of size n, how long to generate output? Express output time in terms of input size, omit constants and take biggest power 9/8/09 7 9/8/09 8 How long will it take? Common big-o times: Constant time O (1) input size doesn t matter Linear time O (n) double input double time Quadratic time O (n 2 ) double input quadruple time ν Exponential time O (2 n ) increment input double time Linear Time Expect most list operations to take linear time O (n) Each step of the recursion can be done in constant time Each step makes only one recursive call List example: multlist, append Integer example: factorial 9/8/09 9 9/8/09 10 Quadratic Time Each non-recursive part of recursive step takes time proportional to input Each step of the recursion makes only one recursive call. List example: # let rec poor_rev list = match list with [] -> [] (x::xs) -> poor_rev xs @ [x];; val poor_rev : 'a list -> 'a list = <fun> Exponential running time Hideous running times on input of any size Each step of recursion takes constant time Each recursion makes two or more recursive calls Easy to write naïve code that is exponential for functions that can be linear 9/8/09 11 9/8/09 12

Exponential running time An Important Optimization # let rec naivefib n = match n with 0 -> 0 1 -> 1 _ -> naivefib (n-1) + naivefib (n-2);; val naivefib : int -> int = <fun> Normal call h g f When a function call is made, the return address needs to be saved to the stack so we know to where to return when the call is finished What if f calls g and g calls h, but calling h is the last thing g does (a tail call)? 9/8/09 13 9/8/09 14 An Important Optimization Tail Recursion Tail call h f When a function call is made, the return address needs to be saved to the stack so we know to where to return when the call is finished What if f calls g and g calls h, but calling h is the last thing g does (a tail call)? Then h can return directly to f instead of g A recursive program is tail recursive if all recursive calls are tail calls Tail recursive programs may be optimized to be implemented as loops, thus removing the function call overhead for the recursive calls Tail recursion generally requires extra accumulator arguments to pass partial results May require an auxiliary function 9/8/09 15 9/8/09 16 Tail Recursion - Example # let rec rev_aux list revlist = match list with [ ] -> revlist x :: xs -> rev_aux xs (x::revlist);; val rev_aux : 'a list -> 'a list -> 'a list = <fun> # let rev list = rev_aux list [ ];; val rev : 'a list -> 'a list = <fun> What is its running time? Comparison poor_rev [1,2,3] = (poor_rev [2,3]) @ [1] = ((poor_rev [3]) @ [2]) @ [1] = (((poor_rev [ ]) @ [3]) @ [2]) @ [1] = (([ ] @ [3]) @ [2]) @ [1]) = ([3] @ [2]) @ [1] = (3:: ([ ] @ [2])) @ [1] = [3,2] @ [1] = 3 :: ([2] @ [1]) = 3 :: (2:: ([ ] @ [1])) = [3, 2, 1] 9/8/09 17 9/8/09 18

Comparison rev [1,2,3] = rev_aux [1,2,3] [ ] = rev_aux [2,3] [1] = rev_aux [3] [2,1] = rev_aux [ ] [3,2,1] = [3,2,1] Folding Functions over Lists How are the following functions similar? # let rec sumlist list = match list with [ ] -> 0 x::xs -> x + sumlist xs;; val sumlist : int list -> int = <fun> # sumlist [2;3;4];; - : int = 9 # let rec prodlist list = match list with [ ] -> 1 x::xs -> x * prodlist xs;; val prodlist : int list -> int = <fun> # prodlist [2;3;4];; - : int = 24 9/8/09 19 9/8/09 20 Folding # let rec fold_left f a list = match list with [] -> a (x :: xs) -> fold_left f (f a x) xs;; val fold_left : ('a -> 'b -> 'a) -> 'a -> 'b list -> 'a = <fun> fold_left f a [x 1 ; x 2 ; ;x n ] = f( (f (f a x 1 ) x 2 ) )x n # let rec fold_right f list b = match list with [ ] -> b (x :: xs) -> f x (fold_right f xs b);; val fold_right : ('a -> 'b -> 'b) -> 'a list -> 'b -> 'b = <fun> fold_right f [x 1 ; x 2 ; ;x n ] b = f x 1 (f x 2 ( (f x n b) )) Folding - Forward Recursion # let sumlist list = fold_right (+) list 0;; val sumlist : int list -> int = <fun> # sumlist [2;3;4];; - : int = 9 # let prodlist list = fold_right ( * ) list 1;; val prodlist : int list -> int = <fun> # prodlist [2;3;4];; - : int = 24 9/8/09 21 9/8/09 22 Folding - Tail Recursion - # let rev list = - fold_left - (fun x -> fun l -> x :: l) //comb op [] //accumulator cell list Folding Can replace recursion by fold_right in any forward primitive recursive definition Primitive recursive means it only recurses on immediate subcomponents of recursive data structure Can replace recursion by fold_left in any tail primitive recursive definition 9/8/09 23 9/8/09 24

Encoding Recursion with Fold # let rec append list1 list2 = match list1 with [ ] -> list2 x::xs -> x :: append xs list2;; val append : 'a list -> 'a list -> 'a list = <fun> Base Case Operation Recursive Call # let append list1 list2 = fold_right (fun x y -> x :: y) list1 list2;; val append : 'a list -> 'a list -> 'a list = <fun> # append [1;2;3] [4;5;6];; - : int list = [1; 2; 3; 4; 5; 6] Mapping What do these functions have in common? # let rec inclist list = match list with [ ] -> [ ] x :: xs -> (1 + x) :: inclist xs;; val inclist : int list -> int list = <fun> # inclist [2;3;4];; - : int list = [3; 4; 5] # let rec doublelist list = match list with [ ] -> [ ] x :: xs -> (2 * x) :: doublelist xs;; val doublelist : int list -> int list = <fun> # doublelist [2;3;4];; - : int list = [4; 6; 8] 9/8/09 25 9/8/09 26 Recall Map # let rec map f list = match list with [] -> [] (h::t) -> (f h) :: (map f t);; val map : ('a -> 'b) -> 'a list -> 'b list = <fun> # map plus_two fib5;; - : int list = [10; 7; 5; 4; 3; 3] Same as List.map Mapping # let inclist = map ((+) 1);; val inclist : int list -> int list = <fun> # inclist [2;3;4];; - : int list = [3; 4; 5] # let doublelist = map (( * ) 2);; val doublelist : int list -> int list = <fun> # doublelist [2;3;4];; - : int list = [4; 6; 8] 9/8/09 27 9/8/09 28 Map from Fold # let map f list = fold_right (fun x y -> f x :: y) list [ ];; val map : ('a -> 'b) -> 'a list -> 'b list = <fun> # map ((+)1) [1;2;3];; - : int list = [2; 3; 4] Can you write fold_right (or fold_left) with just map? How, or why not? Higher Order Functions A function is higher-order if it takes a function as an argument or returns one as a result Example: # let compose f g = fun x -> f (g x);; val compose : ('a -> 'b) -> ('c -> 'a) -> 'c -> 'b = <fun> The type ('a -> 'b) -> ('c -> 'a) -> 'c -> 'b is a higher order type because of ('a -> 'b) and ('c -> 'a) and -> 'c -> 'b 9/8/09 29 9/8/09 30

Thrice Recall: # let thrice f x = f (f (f x));; val thrice : ('a -> 'a) -> 'a -> 'a = <fun> How do you write thrice with compose? Thrice Recall: # let thrice f x = f (f (f x));; val thrice : ('a -> 'a) -> 'a -> 'a = <fun> How do you write thrice with compose? # let thrice f = compose f (compose f f);; val thrice : ('a -> 'a) -> 'a -> 'a = <fun> Is this the only way? 9/8/09 31 9/8/09 32 Partial Application # (+);; - : int -> int -> int = <fun> # (+) 2 3;; - : int = 5 # let plus_two = (+) 2;; val plus_two : int -> int = <fun> # plus_two 7;; - : int = 9 Patial application also called sectioning Lambda Lifting You must remember the rules for evaluation when you use partial application # let add_two = (+) (print_string "test\n"; 2);; test val add_two : int -> int = <fun> # let add2 = (* lambda lifted *) fun x -> (+) (print_string "test\n"; 2) x;; val add2 : int -> int = <fun> 9/8/09 33 9/8/09 34 Lambda Lifting # thrice add_two 5;; - : int = 11 # thrice add2 5;; test test test - : int = 11 Lambda lifting delayed the evaluation of the argument to (+) until the second argument was supplied Partial Application and Unknown Types Recall compose plus_two: # let f1 = compose plus_two;; val f1 : ('_a -> int) -> '_a -> int = <fun> Compare to lambda lifted version: # let f2 = fun g -> compose plus_two g;; val f2 : ('a -> int) -> 'a -> int = <fun> What is the difference? 9/8/09 35 9/8/09 36

Partial Application and Unknown Types Partial Application and Unknown Types _a can only be instantiated once for an expression # f1 plus_two;; - : int -> int = <fun> # f1 List.length;; Characters 3-14: f1 List.length;; ^^^^^^^^^^^ This expression has type 'a list -> int but is here used with type int -> int a can be repeatedly instantiated # f2 plus_two;; - : int -> int = <fun> # f2 List.length;; - : '_a list -> int = <fun> 9/8/09 37 9/8/09 38 Continuations Idea: Use functions to represent the control flow of a program Method: Each procedure takes a function as an argument to which to pass its result; outer procedure returns no result Function receiving the result called a continuation Continuation acts as accumulator for work still to be done Example of Tail Recursion # let rec prod l = match l with [] -> 1 (x :: rem) -> x * prod rem;; val prod : int list -> int = <fun> # let prod list = let rec prod_aux l acc = match l with [] -> acc (y :: rest) -> prod_aux rest (acc * y) (* Uses associativity of multiplication *) in prod_aux list 1;; val prod : int list -> int = <fun> 9/8/09 39 9/8/09 40 Example of Tail Recursion Example of Tail Recursion # let prod list = let rec prod_aux l acc = match l with [] -> acc (y :: rest) -> prod_aux rest (acc * y) in prod_aux list 1;; val prod : int list -> int = <fun> # let prod list = List.fold_left (fun acc -> fun y -> acc * y) 1 list;; val prod : int list -> int = <fun> # let rec app fl x = match fl with [] -> x (f :: rem_fs) -> f (app rem_fs x);; val app : ('a -> 'a) list -> 'a -> 'a = <fun> # let app fs x = let rec app_aux fl acc= match fl with [] -> acc (f :: rem_fs) -> app_aux rem_fs (fun z -> acc (f z)) in app_aux fs (fun y -> y) x;; val app : ('a -> 'a) list -> 'a -> 'a = <fun> 9/8/09 41 9/8/09 42

Continuation Passing Style Writing procedures so that they take a continuation to which to give (pass) the result, and return no result, is called continuation passing style (CPS) Example of Tail Recursion & CSP # let app fs x = let rec app_aux fl acc= match fl with [] -> acc (f :: rem_fs) -> app_aux rem_fs (fun z -> acc (f z)) in app_aux fs (fun y -> y) x;; val app : ('a -> 'a) list -> 'a -> 'a = <fun> # let rec appk fl x k = match fl with [] -> k x (f :: rem_fs) -> appk rem_fs x (fun z -> k (f z));; val appk : ('a -> 'a) list -> 'a -> ('a -> 'b) -> 'b 9/8/09 43 9/8/09 44 Example of CSP # let rec app fl x = match fl with [] -> x (f :: rem_fs) -> f (app rem_fs x);; val app : ('a -> 'a) list -> 'a -> 'a = <fun> # let rec appk fl x k = match fl with [] -> k x (f :: rem_fs) -> appk rem_fs x (fun z -> k (f z));; val appk : ('a -> 'a) list -> 'a -> ('a -> 'b) -> 'b = <fun> Continuation Passing Style A programming technique for all forms of non-local control flow: non-local jumps exceptions general conversion of non-tail calls to tail calls Essentially it s a higher-order function version of GOTO 9/8/09 45 9/8/09 46 Continuation Passing Style A compilation technique to implement nonlocal control flow, especially useful in interpreters. A formalization of non-local control flow in denotational semantics Terms A function is in Direct Style when it returns its result back to the caller. A Tail Call occurs when a function returns the result of another function call without any more computations (eg tail recursion) A function is in Continuation Passing Style when it passes its result to another function. Instead of returning the result to the caller, we pass it forward to another function. 9/8/09 47 9/8/09 48

Example Recursive Functions Simple ing continuation: # let x = (print_int x; print_newline( ) );; val : int -> unit = <fun> Simple function using a continuation: # let plusk a b k = k (a + b) val plusk : int -> int -> (int -> a) -> a = <fun> # plusk 20 22 ;; 42 - : unit = () Recall: # let rec factorial n = if n = 0 then 1 else n * factorial (n - 1);; val factorial : int -> int = <fun> # factorial 5;; - : int = 120 9/8/09 49 9/8/09 50 Recursive Functions # let rec factorial n = if n = 0 then 1 (* Returned value *) else let r = factorial (n - 1) in let a = n * r in a (* Returned value *) ;; val factorial : int -> int = <fun> # factorial 5;; - : int = 120 Recursive Functions # let rec factorialk n k = if n = 0 then k 1 (* Passed value *) else let k1 r = let a = n * r in k a (* Passed value *) in factorialk (n 1) k1 ;; val factorialk : int -> int = <fun> # factorialk 5 ;; 120 - : unit = () 9/8/09 51 9/8/09 52 Recursion Functions # let rec factorialk n k = if n = 0 then k 1 else factorialk (n - 1) (fun r -> k (n * r));; val factorialk : int -> (int -> 'a) -> 'a = <fun> # factorialk 5 ;; 120 - : unit = () Recursive Functions Notice: factorialk is now tail recursive To make recursive call, must built intermediate continuation to take recursive value: m build it to final result: n * m And pass it to final continuation: k (n * m) 9/8/09 53 9/8/09 54

Nesting CPS # let rec lengthk list k = match list with [ ] -> k 0 x :: xs -> lengthk xs (fun r -> k (r + 1));; val lengthk : 'a list -> (int -> 'b) -> 'b = <fun> # let rec lengthk list k = match list with [ ] -> k 0 x :: xs -> lengthk xs (fun r -> plusk r 1 k);; val lengthk : 'a list -> (int -> 'b) -> 'b = <fun> # lengthk [2;4;6;8] ;; 4 - : unit = () Exceptions - Example # exception Zero;; exception Zero # let rec list_mult_aux list = match list with [ ] -> 1 x :: xs -> if x = 0 then raise Zero else x * list_mult_aux xs;; val list_mult_aux : int list -> int = <fun> 9/8/09 55 9/8/09 56 Exceptions - Example # let list_mult list = try list_mult_aux list with Zero -> 0;; val list_mult : int list -> int = <fun> # list_mult [3;4;2];; - : int = 24 # list_mult [7;4;0];; - : int = 0 # list_mult_aux [7;4;0];; Exception: Zero. Exceptions When an exception is raised The current computation is aborted Control is thrown back up the call stack until a matching handler is found All the intermediate calls waiting for a return value are thrown away 9/8/09 57 9/8/09 58 Implementing Exceptions # let multkp m n k = let r = m * n in (print_string "product result: "; print_int r; print_string "\n"; k r);; val multkp : int -> int -> (int -> 'a) -> 'a = <fun> Implementing Exceptions # let rec list_multk_aux list k kexcp = match list with [ ] -> k 1 x :: xs -> if x = 0 then kexcp 0 else list_multk_aux xs (fun r -> multkp x r k) kexcp;; val list_multk_aux : int list -> (int -> 'a) -> (int -> 'a) -> 'a = <fun> # let rec list_multk list k = list_multk_aux list k k;; val list_multk : int list -> (int -> 'a) -> 'a = <fun> 9/8/09 59 9/8/09 60

Implementing Exceptions # list_multk [3;4;2] ;; product result: 2 product result: 8 product result: 24 24 - : unit = () # list_multk [7;4;0] ;; 0 - : unit = () Terminology Tail Position: A subexpression s of expressions e, if it is evaluated, will be taken as the value of e if (x>3) then x + 2 else x - 4 let x = 5 in x + 4 Tail Call: A function call that occurs in tail position if (h x) then f x else (x + g x) 9/8/09 61 9/8/09 62 Terminology Available: A function call that can be executed by the current expression The fastest way to be unavailable is to be guarded by an abstraction (anonymous function). if (h x) then f x else (x + g x) if (h x) then (fun x -> f x) else (g (x + x)) CPS Transformation Step 1: Add continuation argument to any function definition: let f arg = e let f arg k = e Idea: Every function takes an extra parameter saying where the result goes Step 2: A simple expression in tail position should be passed to a continuation instead of returned: return a k a Assuming a is a constant or variable. Simple = No available function calls. 9/8/09 63 9/8/09 64 CPS Transformation Example Step 3: Pass the current continuation to every function call in tail position return f arg f arg k The function isn t going to return, so we need to tell it where to put the result. Step 4: Each function call not in tail position needs to be built into a new continuation (containing the old continuation as appropriate) return op (f arg) f arg (fun r -> k(op r)) op represents a primitive operation Before: let rec add_list lst = match lst with [ ] -> 0 0 :: xs -> add_list xs x :: xs -> (+) x (add_list xs);; After: let rec add_listk lst k = (* rule 1 *) match lst with [ ] -> k 0 (* rule 2 *) 0 :: xs -> add_listk xs k (* rule 3 *) x :: xs -> add_listk xs (fun r -> k ((+) x r));; (* rule 4 *) 9/8/09 65 9/8/09 66

Continuations Example let add a b k = print_string "Add "; k (a + b);; let sub a b k = print_string "Sub "; k (a - b);; let n = print_string "Answer is: "; print_int n; print_newline ();; let idk n k = k n;; type calc = Add of int Sub of int A Small Calculator # let rec eval lst k = match lst with (Add x) :: xs -> eval xs (fun r -> add r x k) (Sub x) :: xs -> eval xs (fun r -> sub r x k) [ ] -> k 0;; # eval [Add 20; Sub 5; Sub 7; Add 3; Sub 5] ;; Sub Add Sub Sub Add Answer is: 6 9/8/09 67 9/8/09 68 Continuations Can Take Multiple Arguments Composing Continations # add 3 5 (fun r -> sub r 2 );; Add Sub Answer is: 6 # add 3 5 (fun r k -> sub r 2 k);; Add - : (int -> _a) -> _a = <fun> # add 3 5 ((fun k r -> sub r 2 k) );; Add Sub Answer is: 6 Problem: Suppose we want to do all additions before any subtractions let ordereval lst k = let rec aux lst ka ks = match lst with (Add x) :: xs -> aux xs (fun r k -> add r x ka k) ks (Sub x) :: xs -> aux xs ka (fun r k -> sub r x ks k) [ ] -> ka 0 ks k in aux lst idk idk 9/8/09 69 9/8/09 70 Sample Run # ordereval [Add 20; Sub 5; Sub 7; Add 3; Sub 5] ;; Add Add Sub Sub Sub Answer is: 6 Execution Trace ordereval [Add 20; Sub 5; Sub 7] aux [Add 20; Sub 5; Sub 7] idk idk aux [Sub 5; Sub 7] (fun r1 k1 -> add 20 r1 idk k1) idk aux [Sub 7] (fun r1 k1 -> add r1 20 idk k1) (fun r2 k2 -> sub r2 5 idk k2) aux [] (fun r1 k1 -> add r1 20 idk k1) 9/8/09 71 9/8/09 72

Execution Trace aux [] (fun r1 k1 -> add r1 20 idk k1) (* Start calling the continuations *) (fun r1 k1 -> add r1 20 idk k1) 0 Execution Trace (fun r1 k1 -> add r1 20 idk k1) 0 add 0 20 idk (* remember idk n k = k n *) 9/8/09 73 9/8/09 74 Execution Trace add 0 20 idk (* remember idk n k = k n *) idk 20 Execution Trace idk 20 20 sub 20 7 (fun r2 k2 -> sub r2 5 idk k2) (fun r2 k2 -> sub r2 5 idk k2) 13 sub 13 5 idk idk 8 ---> 8 9/8/09 75 9/8/09 76