First-class continuations

Slides:



Advertisements
Similar presentations
1 Programming Languages (CS 550) Mini Language Interpreter Jeremy R. Johnson.
Advertisements

Scheme in Scheme. Why implement Scheme in Scheme  Implementing a language is a good way to learn more about programming languages  Interpreters are.
Assignments and Procs w/Params EOPL3 Chapter 4. Expressible vs. Denotable values Expressible Values –the language can express and compute these –represented.
1 Programming Languages (CS 550) Lecture Summary Functional Programming and Operational Semantics for Scheme Jeremy R. Johnson.
Functional Programming. Pure Functional Programming Computation is largely performed by applying functions to values. The value of an expression depends.
Scheme in Scheme?!? …the metacircular evaluator….
Metacircular Evaluation SICP Chapter 4 Mark Boady.
Programming Languages CS 152 Avi Shinnar Lecture 13.
Functional programming: LISP Originally developed for symbolic computing Main motivation: include recursion (see McCarthy biographical excerpt on web site).
Picking and Choosing… …Amb Above the Line. Intro to Nondeterminism So before we programmed to compute an answer to a problem. What if we set up a system.
Lexical vs. Dynamic Scope …where do I point to?. Intro… Remember in Scheme whenever we call a procedure we pop a frame and point it to where the procedure.
6.001 SICP SICP – Evaluation I Recitation 11/19/2004 Eval review Evaluation examples define lambda apply New language elements.
Functional programming: LISP Originally developed for symbolic computing First interactive, interpreted language Dynamic typing: values have types, variables.
1 Saves repeated renaming and substitution: explicit substitution is replaced by variable bindings using new data structures (frame, environment). Can.
CS220 Programming Principles 프로그래밍의 이해 2002 가을학기 Class 17: Nondeterministic Computing 한 태숙.
1 Programming Languages (CS 550) Lecture 4 Summary Functional Programming and Operational Semantics for Scheme Jeremy R. Johnson.
1 Env. Model Implementation & Analyzer Practice Session #10 Env. Model Implementation & Analyzer Practice Session #10.
PPL CPS. Moed A 2007 Solution (define scale-tree (λ (tree factor) (map (λ (sub-tree) (if (list? sub-tree) (scale-tree sub-tree factor) (* sub-tree.
Logic Programming (Control and Backtracking). Contents Logic Programming –sans Control –with Backtracking Streams.
CS61A Lecture Colleen Lewis 1. Clicker poll Are you in a two person group for project 4? A)Yes – I have my partner B)No – I plan to work.
PPL CPS. Moed A 2007 Solution (define scale-tree (λ (tree factor) (map (λ (sub-tree) (if (list? sub-tree) (scale-tree sub-tree factor) (* sub-tree.
CSE 341 Lecture 21 delayed evaluation; thunks; streams slides created by Marty Stepp
Operational Semantics of Scheme
Additional Scheme examples
CSE341: Programming Languages Lecture 14 Thunks, Laziness, Streams, Memoization Dan Grossman Spring 2017.
6.001 Jeopardy.
6.001 SICP Variations on a Scheme
6.001 SICP Compilation Context: special purpose vs. universal machines
The Environment Model*
September 4, 1997 Programming Languages (CS 550) Lecture 6 Summary Operational Semantics of Scheme using Substitution Jeremy R. Johnson TexPoint fonts.
Lecture 14 - Environment Model (cont.) - Mutation - Stacks and Queues
CSE341: Programming Languages Lecture 14 Thunks, Laziness, Streams, Memoization Dan Grossman Winter 2013.
Closures and Streams cs784(Prasad) L11Clos
COP4020 Programming Languages
Implementing Recursion
Env. Model Implementation
Closure conversion Compiling λ.
Nondeterministic Evaluation
CSE341: Programming Languages Lecture 14 Thunks, Laziness, Streams, Memoization Zach Tatlock Winter 2018.
6.001 SICP Data abstractions
CSE341: Programming Languages Lecture 14 Thunks, Laziness, Streams, Memoization Dan Grossman Spring 2013.
The Metacircular Evaluator
CSE341: Programming Languages Lecture 14 Thunks, Laziness, Streams, Memoization Dan Grossman Spring 2016.
Dynamic Scoping Lazy Evaluation
The Metacircular Evaluator
Abstract machines & interpreters
And more store-passing interpreters
CSE341: Programming Languages Lecture 14 Thunks, Laziness, Streams, Memoization Dan Grossman Autumn 2017.
CSE341: Programming Languages Lecture 14 Thunks, Laziness, Streams, Memoization Dan Grossman Autumn 2018.
Winter 2018 With thanks to: Dan Grossman / Eric Mullen
Continuation-passing Style (CPS)
The Metacircular Evaluator (Continued)
Continuation Marks A john clements talk.
6.001 SICP Further Variations on a Scheme
Explicit Application of Procedures, and Explicit Evaluation
Extending our interpreted language with Scheme’s set!
Streams, Delayed Evaluation and a Normal Order Interpreter
Lecture 12: Message passing The Environment Model
Lecture 14 - Environment Model (cont.) - Mutation - Stacks and Queues
6.001 SICP Variations on a Scheme
Dan Grossman / Eric Mullen Autumn 2017
Announcements Quiz 5 HW6 due October 23
6.001 SICP Interpretation Parts of an interpreter
Assignments and Procs w/Params
topics interpreters meta-linguistic abstraction eval and apply
Recursive Procedures and Scopes
Rehearsal: Lazy Evaluation Infinite Streams in our lazy evaluator
More Scheme CS 331.
Brett Wortzman Summer 2019 Slides originally created by Dan Grossman
CSE341: Programming Languages Lecture 14 Thunks, Laziness, Streams, Memoization Dan Grossman Spring 2019.
Presentation transcript:

First-class continuations call/cc, stack-passing CEK machines

But first… Assignment 2

e ::= (letrec* ([x e] ...) e)     | (let* ([x e] ...) e)     | (let ([x e] ...) e)     | (let x ([x e] ...) e)     | (lambda (x ...) e)     | (lambda x e)     | (lambda (x ...+ . x) e)     | (dynamic-wind e e e)     | (guard (x cond-clause …) e)     | (raise e)     | (delay e)     | (force e)     | (and e ...)     | (or e ...)     | (cond cond-clause ...)     | (case e case-clause ...)     | (if e e e)     | (when e e)     | (unless e e)     | (set! x e)     | (begin e ...+)     | (call/cc e)     | (apply e e)     | (e e ...)     | x     | op     | (quote dat)     | (case e case-clause ...)     | (if e e e)     | (when e e)     | (unless e e)     | (set! x e)     | (begin e ...+)     | (call/cc e)     | (apply e e)     | (e e ...)     | x     | op     | (quote dat)

e ::= (let ([x e] ...) e)     | (lambda (x ...) e)     | (lambda x e)     | (apply e e)     | (e e ...)     | (prim op e …) | (apply-prim op e)     | (if e e e)     | (set! x e)     | (call/cc e)     | x     | (quote dat)

utils.rkt prim? reserved? scheme-exp? ir-exp? eval-scheme eval-ir

Write your own tests ./tests/*/mytest.scm (require “utils.rkt”) (require “desugar.rkt”) (define scm (read (open-input-file “…”))) (scheme-exp? scm) scm (define ir (desugar scm)) (ir-exp? ir) ir (eval-ir ir)

(solved with only prims and quote) start-0 (solved with only prims and quote) (+ ‘5 ‘6) (prim + ‘5 ‘6)

(solved once you add forms in both langs such as let, …) start-1 (solved once you add forms in both langs such as let, …) (let ([x ‘1] (let ([_ (set! x ‘2)]) … (+ x y) …))

(unless e0 e1) (if e0 (void) e1)

(let ([t ek]) (if (memv t ‘(d0 d1)) ebdy (case t clauses …))) (case ek [(d0 d1) ebdy] clauses …) (let ([t ek]) (if (memv t ‘(d0 d1)) ebdy (case t clauses …)))

(delay e) | (force e) | promise? promises (delay e) | (force e) | promise? Delay wraps its body in a thunk to be executed later by a force form. A promise is returned. Prim promise? should desugar correctly. Forcing a promise evaluates and saves the value.

call/cc

((((λ (u) (u u)) (λ (a) a)) e1) e0)

r = ((λ (u) (u u)) (λ (a) a)) ((((λ (u) (u u)) (λ (a) a)) e1) e0) ℰ = ((□ e1) e0) r = ((λ (u) (u u)) (λ (a) a))

r = ((λ (u) (u u)) (λ (a) a)) ((((λ (u) (u u)) (λ (a) a)) e1) e0) ℰ = ((□ e1) e0) r = ((λ (u) (u u)) (λ (a) a)) (λ (z) (((λ (u) (u u)) (λ (a) a)) z))

η - expansion & thunking allows us to take hold of a suspended first-class computation (a call site) we may apply later.

call/cc allows us to take hold of a suspended first-class computation (a return point) we may apply later.

r = (call/cc (λ (k) (k (λ …)))) (((call/cc (λ (k) (k (λ …)))) e1) e0) ℰ = ((□ e1) e0) r = (call/cc (λ (k) (k (λ …)))) → ((λ (k) (k (λ …))) (λ (□) ((□ e1) e0))) → ((λ (□) ((□ e1) e0))) (λ …)) → (((λ …) e1) e0)))

Example 1. Preemptive function return

(define (fun x) (let ([y (if (p? x) … …)]) (g x y)))

(define (fun x) (call/cc (lambda (return) (let ([y (if (p? x) … (return x))]) (g x y)))))

Example 2. Coroutines / cooperative threading Suggested exercise.

(lambda (yield) (let loop ([n 0])) (yield n) (loop (+ n 1)))

(define (coroutine->gen co) (define resume co) (lambda () (call/cc (lambda (return) (define yield (lambda (v) (call/cc (lambda (r) (set! resume r) (return v))))) (resume yield)))))

Example 3. Backtracking search

(let ([a (amb '(1 2 3 4 5 6))] [b (amb '(1 2 3 4 5 6))] [c (amb '(1 2 3 4 5 6))]) ;(pretty-print `(trying ,a ,b ,c)) (assert (= (+ (* a a) (* b b)) (* c c))) `(solution ,a ,b ,c))

(define (amb lst) (let ([cc (call/cc (lambda (u) (u u)))]) (if (null? lst) (fail) (let ([head (car lst)]) (set! lst (cdr lst)) (set! ccstack (cons cc ccstack)) head))))

(define ccstack ‘()) (define (fail) (if (null? ccstack) (error 'no-solution) (let ([next-cc (car ccstack)]) (set! ccstack (cdr ccstack)) (next-cc next-cc)))) (define (assert t) (if t (void) (fail)))

dynamic-wind & call/cc

(dynamic-wind e0 e1 e2)

(dynamic-wind (lambda () entry code) (lambda () body) (lambda () exit code))

call/cc opens source file and scans to last position closes file

call/cc read-k opens source file and scans to last position closes file

call/cc (read-k write-k) opens target file and scans to last position closes file opens source file and scans to last position closes file

Exceptions (guard [x cond-clause …] e) | (raise e) Guard obtains the current continuation and then uses dynamic wind to set / unset a current handler Raise simply invokes the current handler on a value Guard’s continuation should be outside the dynamic-wind so repeated raises don’t infinite loop! Wrap exceptions in a cons cell if using this idiom:

(raise e) => (%handler (cons e ‘())) (guard [x clauses…] body) => (let ([cc (call/cc (lambda (k) k))]) (if (cons? cc) ; handle the raised exception (dynamic-wind setup-new-handler (lambda () body) revert-to-old-handler)))

Stack-passing (CEK) semantics

Store-passing machine Control-expression Term-rewriting / textual reduction Context and redex for deterministic eval CE Control & Env machine Big-step, explicit closure creation CES Store-passing machine Passes addr->value map in evaluation order CEK Stack-passing machine Passes a list of stack frames, small-step

(e0, env) ⇓ ((λ (x) e2), env’) (e1, env) ⇓ v1 (e2, env’[x ↦ v1]) ⇓ v2 ((e0 e1), env) ⇓ v2 ((λ (x) e), env) ⇓ ((λ (x) e), env) (x, env) ⇓ env(x)

Previously… (e0 e1), env e’, env’

Previously… e0 e1 (e0 e1), env e’, env’

e ::= (λ (x) e)     | (e e)     | x | (call/cc (λ (x) e))

e ::= (λ (x) e) | (e e) | x | (call/cc (λ (x) e)) k ::= () | ar(e, env, k) | fn(v, k) e ::= (λ (x) e)     | (e e)     | x | (call/cc (λ (x) e))

ℰ ::= (ℰ e) | (v ℰ) | □ e ::= (λ (x) e) | (e e) | x k ::= () | ar(e, env, k) | fn(v, k) e ::= (λ (x) e)     | (e e)     | x | (call/cc (λ (x) e)) ℰ ::= (ℰ e) | (v ℰ) | □

((e0 e1), env, k) → (e0, env, ar(e1, env, k)) (x, env, ar(e1, env1, k1)) → (e1, env1, fn(env(x), k1)) ((λ (x) e), env, ar(e1, env1, k1)) → (e1, env1, fn(((λ (x) e), env), k1)) (x, env, fn(((λ (x1) e1), env1), k1)) → (e1, env1[x1 ↦ env(x)], k1) ((λ (x) e), env, fn(((λ (x1) e1), env1), k1)) → (e1, env1[x1 ↦ ((λ (x) e), env)], k1)

call/cc semantics ((call/cc (λ (x) e0)), env, k) → (e0, env[x ↦ k], k) ((λ (x) e0), env, fn(k0, k1)) → ((λ (x) e0), env, k0) (x, env, fn(k0, k1)) → (x, env, k0)

(x, env, let(x1, e1, env1, k1)) → (e1, env1[x1 ↦ env(x)], k1) e ::= ... | (let ([x e0]) e1) k ::= … | let(x, e, env, k) (x, env, let(x1, e1, env1, k1)) → (e1, env1[x1 ↦ env(x)], k1) ((λ (x) e), env, let(x1, e1, env1, k1)) → (e1, env1[x1 ↦ ((λ (x) e), env)], k1)

e ::= ... | (let ([x e0]) e1) k ::= … | let(x, e, env, k) (x, env, fn(((λ (x1) e1), env1), k1)) → (e1, env1[x1 ↦ env(x)], k1) ((λ (x) e), env, fn(((λ (x1) e1), env1), k1)) → (e1, env1[x1 ↦ ((λ (x) e), env)], k1) k ::= … | let(x, e, env, k) (x, env, let(x1, e1, env1, k1)) → (e1, env1[x1 ↦ env(x)], k1) ((λ (x) e), env, let(x1, e1, env1, k1)) → (e1, env1[x1 ↦ ((λ (x) e), env)], k1)

CEK-machine evaluation → … → (x, env, ()) → env(x)

Implementing dynamic-wind

; Finds the maximum shared tail of two lists (define (%common-tail st0 st1) (let ([lx (length x)] [ly (length y)]) (let loop ([x (if (> lx ly) (drop x (- lx ly)) x)] [y (if (> ly lx) (drop y (- ly lx)) y)]) (if (eq? x y) x (loop (cdr x) (cdr y))))))

; Winds down old stack and up new stack, ; invoking the proper post and then pre thunks as it winds (define (%do-wind new-stack) (unless (eq? new-stack %wind-stack) (let ([tail (%common-tail new-stack %wind-stack)]) (let loop ([st %wind-stack]) (unless (eq? st tail) (set! %wind-stack (cdr st)) ((cdr (car st))) (loop (cdr st)))) (let loop ([st new-stack]) (loop (cdr st))) ((car (car st))) (set! %wind-stack st)))))

(define %wind-stack ‘()) (define (dynamic-wind pre body post) (pre) (set! %wind-stack (cons (cons pre post) %wind-stack)) (let ([val (body)]) (set! %wind-stack (cdr %wind-stack)) (post) v))

(define (desugar-t e) (match e ... ; desugar call/cc so that each use saves the stack & ; wraps resulting continuation with a call to %do-wind [`(call/cc ,e0) `(call/cc ,(desugar-t e0))] ...))

(define (desugar-t e) (match e ... ; desugar call/cc so that each use saves the stack & ; wraps resulting continuation with a call to %do-wind [`(call/cc ,e0) `(call/cc ,(desugar-t `(lambda (k) (,e0 (lambda (x) (k x))))))] ...))

(define (desugar-t e) (match e ... ; desugar call/cc so that each use saves the stack & ; wraps resulting continuation with a call to %do-wind [`(call/cc ,e0) `(call/cc ,(desugar-t `(lambda (k) ; save k and k’s stack (,e0 (let ([k-stack %wind-stack]) (lambda (x) (begin (%do-wind k-stack) (k x))))))))] ...))