lecture 14 evaluation contexts first-class continuations ... · small-step semantics (old) and...

27
CS152: Programming Languages Lecture 14 — Evaluation Contexts First-Class Continuations Lambda Interpreters Continuation-Passing Style Dan Grossman Spring 2011

Upload: others

Post on 06-Aug-2020

2 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: Lecture 14 Evaluation Contexts First-Class Continuations ... · Small-step semantics (old) and evaluation-context semantics (new) are very similar: I Totally equivalent step sequence

CS152: Programming Languages

Lecture 14 —Evaluation Contexts

First-Class ContinuationsLambda Interpreters

Continuation-Passing Style

Dan GrossmanSpring 2011

Page 2: Lecture 14 Evaluation Contexts First-Class Continuations ... · Small-step semantics (old) and evaluation-context semantics (new) are very similar: I Totally equivalent step sequence

Gimme A Break (from types)

We have more to do with type systems:

I Parametric Polymorphism

I Recursive Types

I Type-And-Effect Systems

But sometimes it’s more fun to mix up the lecture schedule

This lecture: Related topics that work in typed or untypedsettings:

I How operational semantics can be defined more concisely

I How lambda-calculus (or PLs) can be enriched with powerfulcontrol operators

I How lambda-calculus can be efficiently implemented

I Cool programming idioms related to these concepts

Dan Grossman CS152 Spring 2011, Lecture 14 2

Page 3: Lecture 14 Evaluation Contexts First-Class Continuations ... · Small-step semantics (old) and evaluation-context semantics (new) are very similar: I Totally equivalent step sequence

Toward Evaluation Contexts

λ-calculus with extensions has many “boring inductive rules”:

e1 → e′1e1 e2 → e′1 e2

e2 → e′2v e2 → v e′2

e→ e′

A(e)→ A(e′)

e→ e′

B(e)→ B(e′)

e1 → e′1(e1, e2)→ (e′1, e2)

e2 → e′2(v1, e2)→ (v1, e

′2)

e→ e′

e.1→ e′.1

e→ e′

e.2→ e′.2

e→ e′

match e with Ax. e1 | By. e2 → match e′ with Ax. e1 | By. e2

And some “interesting do-work rules”:

(λx. e) v → e[v/x] (v1, v2).1→ v1 (v1, v2).2→ v2

match A(v) with Ax. e1 | By. e2 → e1[v/x]

match B(v) with Ay. e1 | Bx. e2 → e2[v/x]

Dan Grossman CS152 Spring 2011, Lecture 14 3

Page 4: Lecture 14 Evaluation Contexts First-Class Continuations ... · Small-step semantics (old) and evaluation-context semantics (new) are very similar: I Totally equivalent step sequence

Evaluation Contexts

Define evaluation contexts, which are expressions with one holewhere “interesting work” is allowed to occur:

E ::= [·] | E e | v E | (E, e) | (v, E) | E.1 | E.2| A(E) | B(E) | (match E with Ax. e1 | By. e2)

Define “filling the hole” E[e] in the obvious way (stapling)I A metafunction of type EvalContext→Exp→Exp

Semantics: Use two judgments

I e→ e′ with 1 rule:e

p→ e′

E[e]→ E[e′]

I ep→ e′ with all the “interesting work”:

(λx. e) vp→ e[v/x] (v1, v2).1

p→ v1 (v1, v2).2p→ v2

match A(v) with Ax. e1 | By. e2p→ e1[v/x]

match B(v) with Ay. e1 | Bx. e2p→ e2[v/x]

Dan Grossman CS152 Spring 2011, Lecture 14 4

Page 5: Lecture 14 Evaluation Contexts First-Class Continuations ... · Small-step semantics (old) and evaluation-context semantics (new) are very similar: I Totally equivalent step sequence

Evaluation Contexts

Define evaluation contexts, which are expressions with one holewhere “interesting work” is allowed to occur:

E ::= [·] | E e | v E | (E, e) | (v, E) | E.1 | E.2| A(E) | B(E) | (match E with Ax. e1 | By. e2)

Define “filling the hole” E[e] in the obvious way (stapling)I A metafunction of type EvalContext→Exp→Exp

Semantics: Use two judgments

I e→ e′ with 1 rule:e

p→ e′

E[e]→ E[e′]

I ep→ e′ with all the “interesting work”:

(λx. e) vp→ e[v/x] (v1, v2).1

p→ v1 (v1, v2).2p→ v2

match A(v) with Ax. e1 | By. e2p→ e1[v/x]

match B(v) with Ay. e1 | Bx. e2p→ e2[v/x]

Dan Grossman CS152 Spring 2011, Lecture 14 4

Page 6: Lecture 14 Evaluation Contexts First-Class Continuations ... · Small-step semantics (old) and evaluation-context semantics (new) are very similar: I Totally equivalent step sequence

Evaluation Contexts

Define evaluation contexts, which are expressions with one holewhere “interesting work” is allowed to occur:

E ::= [·] | E e | v E | (E, e) | (v, E) | E.1 | E.2| A(E) | B(E) | (match E with Ax. e1 | By. e2)

Define “filling the hole” E[e] in the obvious way (stapling)I A metafunction of type EvalContext→Exp→Exp

Semantics: Use two judgments

I e→ e′ with 1 rule:e

p→ e′

E[e]→ E[e′]

I ep→ e′ with all the “interesting work”:

(λx. e) vp→ e[v/x] (v1, v2).1

p→ v1 (v1, v2).2p→ v2

match A(v) with Ax. e1 | By. e2p→ e1[v/x]

match B(v) with Ay. e1 | Bx. e2p→ e2[v/x]

Dan Grossman CS152 Spring 2011, Lecture 14 4

Page 7: Lecture 14 Evaluation Contexts First-Class Continuations ... · Small-step semantics (old) and evaluation-context semantics (new) are very similar: I Totally equivalent step sequence

Evaluation Contexts

Define evaluation contexts, which are expressions with one holewhere “interesting work” is allowed to occur:

E ::= [·] | E e | v E | (E, e) | (v, E) | E.1 | E.2| A(E) | B(E) | (match E with Ax. e1 | By. e2)

Define “filling the hole” E[e] in the obvious way (stapling)I A metafunction of type EvalContext→Exp→Exp

Semantics: Use two judgments

I e→ e′ with 1 rule:e

p→ e′

E[e]→ E[e′]

I ep→ e′ with all the “interesting work”:

(λx. e) vp→ e[v/x] (v1, v2).1

p→ v1 (v1, v2).2p→ v2

match A(v) with Ax. e1 | By. e2p→ e1[v/x]

match B(v) with Ay. e1 | Bx. e2p→ e2[v/x]

Dan Grossman CS152 Spring 2011, Lecture 14 4

Page 8: Lecture 14 Evaluation Contexts First-Class Continuations ... · Small-step semantics (old) and evaluation-context semantics (new) are very similar: I Totally equivalent step sequence

Evaluation Contexts

Define evaluation contexts, which are expressions with one holewhere “interesting work” is allowed to occur:

E ::= [·] | E e | v E | (E, e) | (v, E) | E.1 | E.2| A(E) | B(E) | (match E with Ax. e1 | By. e2)

Define “filling the hole” E[e] in the obvious way (stapling)I A metafunction of type EvalContext→Exp→Exp

Semantics: Use two judgments

I e→ e′ with 1 rule:e

p→ e′

E[e]→ E[e′]

I ep→ e′ with all the “interesting work”:

(λx. e) vp→ e[v/x] (v1, v2).1

p→ v1 (v1, v2).2p→ v2

match A(v) with Ax. e1 | By. e2p→ e1[v/x]

match B(v) with Ay. e1 | Bx. e2p→ e2[v/x]

Dan Grossman CS152 Spring 2011, Lecture 14 4

Page 9: Lecture 14 Evaluation Contexts First-Class Continuations ... · Small-step semantics (old) and evaluation-context semantics (new) are very similar: I Totally equivalent step sequence

Evaluation Contexts

Define evaluation contexts, which are expressions with one holewhere “interesting work” is allowed to occur:

E ::= [·] | E e | v E | (E, e) | (v, E) | E.1 | E.2| A(E) | B(E) | (match E with Ax. e1 | By. e2)

Define “filling the hole” E[e] in the obvious way (stapling)I A metafunction of type EvalContext→Exp→Exp

Semantics: Use two judgments

I e→ e′ with 1 rule:e

p→ e′

E[e]→ E[e′]

I ep→ e′ with all the “interesting work”:

(λx. e) vp→ e[v/x] (v1, v2).1

p→ v1 (v1, v2).2p→ v2

match A(v) with Ax. e1 | By. e2p→ e1[v/x]

match B(v) with Ay. e1 | Bx. e2p→ e2[v/x]

Dan Grossman CS152 Spring 2011, Lecture 14 4

Page 10: Lecture 14 Evaluation Contexts First-Class Continuations ... · Small-step semantics (old) and evaluation-context semantics (new) are very similar: I Totally equivalent step sequence

Decomposition

Evaluation relies on decomposition (unstapling the correct subtree)

I Given e, find E, ea, e′a such that e = E[ea] and eap→ e′a

Theorem (Unique Decomposition): There is at most onedecomposition of e

I Hence evaluation is deterministic since at most one primitivestep can apply to any expression

Theorem (Progress, restated): If e is well-typed, then there is adecomposition or e is a value

Dan Grossman CS152 Spring 2011, Lecture 14 5

Page 11: Lecture 14 Evaluation Contexts First-Class Continuations ... · Small-step semantics (old) and evaluation-context semantics (new) are very similar: I Totally equivalent step sequence

Evaluation Contexts: So what?

Small-step semantics (old) and evaluation-context semantics (new)are very similar:

I Totally equivalent step sequenceI (made both left-to-right call-by-value)

I Just rearranged things to be more concise: Each boring rulebecame a form of E

I Both “work” the same way:I Find the next place in the program to take a “primitive step”I Take that stepI Plug the result into the rest of the programI Repeat (next “primitive step” could be somewhere else) until

you can’t anymore (value or stuck)

Evaluation contexts so far just cleanly separate the “find and plug”from the “take that step” by building an explicit E

Dan Grossman CS152 Spring 2011, Lecture 14 6

Page 12: Lecture 14 Evaluation Contexts First-Class Continuations ... · Small-step semantics (old) and evaluation-context semantics (new) are very similar: I Totally equivalent step sequence

Continuations

Now that we have defined E explicitly in our metalanguage, whatif we also put it on our language

I From metalanguage to language is called reification

First-class continuations in one slide:

e ::= . . . | letcc x. e | throw e e | cont Ev ::= . . . | cont EE ::= . . . | throw E e | throw v E

E[letcc x. e]→ E[(λx. e)(cont E)] E[throw (cont E′) v]→ E′[v]

I New operational rules for→ notp→ because “the E matters”

I letcc x. e grabs the current evaluation context (“the stack”)

I throw (cont E′) v restores old context: “jump somewhere”

I cont E not in source programs: “saved stack (value)”

Dan Grossman CS152 Spring 2011, Lecture 14 7

Page 13: Lecture 14 Evaluation Contexts First-Class Continuations ... · Small-step semantics (old) and evaluation-context semantics (new) are very similar: I Totally equivalent step sequence

Examples (exceptions-like)

1 + (letcc k. 2 + 3)→∗ 6

1 + (letcc k. 2 + (throw k 3))→∗ 4

1 + (letcc k. (throw k (2 + 3)))→∗ 6

1 + (letcc k. (throw k (throw k (throw k 2))))→∗ 3

Dan Grossman CS152 Spring 2011, Lecture 14 8

Page 14: Lecture 14 Evaluation Contexts First-Class Continuations ... · Small-step semantics (old) and evaluation-context semantics (new) are very similar: I Totally equivalent step sequence

Example (“time travel”)

Caml doesn’t have first-class continuations, but if it did:

let valOf x = match x with None-> failwith "" |Some x-> x

let x = ref true (* avoids infinite loop )

let g = ref None

let y = ref (1 + 2 + (letcc k. (g := Some k); 3))

let z = if !x

then (x := false; throw (valOf (!g)) 7)

else !y

SML/NJ does: This runs and binds 10 to z:

open SMLofNJ.Cont

val x = ref true (* avoids infinite loop *)

val g : int cont option ref = ref NONE

val y = ref (1 + 2 + (callcc (fn k => ((g := SOME k); 3))))

val z = if !x then (x := false; throw (valOf (!g)) 7) else !y

Dan Grossman CS152 Spring 2011, Lecture 14 9

Page 15: Lecture 14 Evaluation Contexts First-Class Continuations ... · Small-step semantics (old) and evaluation-context semantics (new) are very similar: I Totally equivalent step sequence

Is this useful?

First-class continuations are a single construct sufficient for:

I Exceptions

I Cooperative threads (including coroutines)I “yield” captures the continuation (the “how to resume me”)

and gives it to the scheduler (implemented in the language),which then throws to another thread’s “how to resume me”

I Other crazy thingsI Often called the “goto of functional programming” —

incredibly powerful, but nonstandard uses are usuallyinscrutable

I Key point is that we can “jump back in” unlike boring-oldexceptions

Dan Grossman CS152 Spring 2011, Lecture 14 10

Page 16: Lecture 14 Evaluation Contexts First-Class Continuations ... · Small-step semantics (old) and evaluation-context semantics (new) are very similar: I Totally equivalent step sequence

Another view

If you’re confused, think call stacks:

I What if your favorite language had operations for:I Store current stack in xI Replace current stack with stack in x

I “Resume the stack’s hole” with something different or whenmutable state is different

I Else you are sure to have an infinite loop since you will laterresume the stack again

Dan Grossman CS152 Spring 2011, Lecture 14 11

Page 17: Lecture 14 Evaluation Contexts First-Class Continuations ... · Small-step semantics (old) and evaluation-context semantics (new) are very similar: I Totally equivalent step sequence

Where are we

Done:

I Redefined our operational semantics using evaluation contexts

I That made it easy to define first-class continuations

I Example uses of continuations

Now:I Implement an efficent lambda-calculus interpreter using little

more than malloc and a single while-loopI Explicit evaluation contexts (i.e., continuations) is essentialI Key novelty is maintaining the current context incrementallyI letcc and throw can be O(1) operations (homework 4)

I Can achieve the same effect as a source-to-sourcetransformation

I The continuation-passing-style (CPS) transformationI Programming in explicit CPS-style is also a powerful idiom

Dan Grossman CS152 Spring 2011, Lecture 14 12

Page 18: Lecture 14 Evaluation Contexts First-Class Continuations ... · Small-step semantics (old) and evaluation-context semantics (new) are very similar: I Totally equivalent step sequence

See the code

See lec14_interp.ml for four interpreters where each is:

I More efficient than the previous one and relies on less fromthe meta-language

I Close enough to the previous one that equivalence amongthem is tractable to prove

The interpreters:

1. Plain-old small-step with substitution

2. Evaluation contexts, re-decomposing at each step

3. Incremental decomposition, made efficient by representingevaluation contexts (i.e., continuations) as a linked list with“shallow end” of the stack at the beginning of the list

4. Replacing substitution with environments

The last interpreter is trivial to port to assembly or C

Dan Grossman CS152 Spring 2011, Lecture 14 13

Page 19: Lecture 14 Evaluation Contexts First-Class Continuations ... · Small-step semantics (old) and evaluation-context semantics (new) are very similar: I Totally equivalent step sequence

Example

Small-step (first interpreter):

Decomposition (second interpreter):

Dan Grossman CS152 Spring 2011, Lecture 14 14

Page 20: Lecture 14 Evaluation Contexts First-Class Continuations ... · Small-step semantics (old) and evaluation-context semantics (new) are very similar: I Totally equivalent step sequence

Example

Decomposition (second interpreter):

Decomposition rewritten with linked list (hole implicit at front):

Dan Grossman CS152 Spring 2011, Lecture 14 15

Page 21: Lecture 14 Evaluation Contexts First-Class Continuations ... · Small-step semantics (old) and evaluation-context semantics (new) are very similar: I Totally equivalent step sequence

Example

Decomposition rewritten with linked list (hole implicit at front):

Some loop iterations of third interpreter:

Fourth interpreter: replace substitution with environment/closuresDan Grossman CS152 Spring 2011, Lecture 14 16

Page 22: Lecture 14 Evaluation Contexts First-Class Continuations ... · Small-step semantics (old) and evaluation-context semantics (new) are very similar: I Totally equivalent step sequence

The end result

The last interpreter needs just:

I A loop

I Lists for contexts and environments

I Tag tests

Moreover:

I Function calls execute in O(1) timeI Variable look-ups don’t, but that’s fixable

I (e.g., de Bruijn indices and arrays for environments)

I Other operations, including pairs, conditionals, letcc, andthrow also all work in O(1) time

I Need new kinds of contexts and valuesI Last problem of homework 4

Making evaluation contexts explicit data structures was key

Dan Grossman CS152 Spring 2011, Lecture 14 17

Page 23: Lecture 14 Evaluation Contexts First-Class Continuations ... · Small-step semantics (old) and evaluation-context semantics (new) are very similar: I Totally equivalent step sequence

Toward the CPS transform

Rather than a fancy abstract machine where...

I The metalanguage doesn’t need recursion (a call-stack)

I letcc/throw are easy-to-encode O(1) operations

... can achieve the same effect via a whole-program translationinto a sublanguage (source-to-source transformation)

I No expressions with nontrivial evaluation contexts

I Every expression becomes a continuation-accepting function

I Never “return” — instead call the current continuation

Dan Grossman CS152 Spring 2011, Lecture 14 18

Page 24: Lecture 14 Evaluation Contexts First-Class Continuations ... · Small-step semantics (old) and evaluation-context semantics (new) are very similar: I Totally equivalent step sequence

The CPS transformation (one way to do it)

A metafunction from expressions to expressions

Example source language (other features similar):

e ::= x | λx. e | e e | c | e+ ev ::= x | λx. e | c

CPSE(v) = λk. k CPSV(v)CPSE(e1 + e2) = λk. CPSE(e1) λx1. CPSE(e2) λx2. k (x1+x2)

CPSE(e1 e2) = λk. CPSE(e1) λf. CPSE(e2) λx. f x k

CPSV(c) = cCPSV(x) = x

CPSV(λx. e) = λx. λk. CPSE(e) k

To run the whole program e, do CPSE(e) λx. x

Dan Grossman CS152 Spring 2011, Lecture 14 19

Page 25: Lecture 14 Evaluation Contexts First-Class Continuations ... · Small-step semantics (old) and evaluation-context semantics (new) are very similar: I Totally equivalent step sequence

Result of the CPS transformation

I Correctness: e is equivalent to CPSE(e) λx. x

I If whole program has type τP and e has type τ , thenCPSE(e) has type (τ → τP )→ τP

I Fixes evaluation order: CPSE(e) will evaluate e inleft-to-right call-by-value

I Other similar transformations encode other evaluation ordersI Every intermediate computation is bound to a variable (helpful

for compiler writers)

I For all e, evaluation of CPSE(e) stays in this sublanguage:

e ::= v | v v | v v v | v (v + v)v ::= x | λx. e | c

I Hence no need for a call-stack: every call is a tail-callI Now the program is maintaining the evaluation context via a

closure that has the next “link” in its environment that has thenext “link” in its environment, etc.

Dan Grossman CS152 Spring 2011, Lecture 14 20

Page 26: Lecture 14 Evaluation Contexts First-Class Continuations ... · Small-step semantics (old) and evaluation-context semantics (new) are very similar: I Totally equivalent step sequence

Encoding first-class continuations

If you apply the CPS transform, then letcc and throw can becomeO(1) operations encodable in the source language

CPSE(letcc k. e) = λk. CPSE(e) kCPSE(throw e1 e2) = λk. CPSE(e1) λx1. CPSE(e2) λx2. x1 x2︸ ︷︷ ︸

or just x1

I letcc gets passed the current continuation just as it needs

I throw ignores the current continuation just as it should

You can also manually program in this style (fully or partially)

I Has other uses as a programming idiom too...

Dan Grossman CS152 Spring 2011, Lecture 14 21

Page 27: Lecture 14 Evaluation Contexts First-Class Continuations ... · Small-step semantics (old) and evaluation-context semantics (new) are very similar: I Totally equivalent step sequence

A useful advanced programming idiom

I A first-class continuation can “reify session state” in aclient-server interaction

I If the continuation is passed to the client, which returns itlater, then the server can be stateless

I Suggests CPS for web programmingI Better: tools that do the CPS transformation for you

I Gives you a “prompt-client” primitive without server-side state

I Because CPS uses only tail calls, it avoids deep call stackswhen traversing recursive data structures

I See lec14_tailcalls.ml for this and related idioms

In short, “thinking in terms of CPS” is a powerful technique fewprogrammers have

Dan Grossman CS152 Spring 2011, Lecture 14 22