Continuations. MacrosThe continuation passing style. Continuations in RACKET.
Macro definitions
May 8, 2019
Continuations. Macros
The continuation passing style
The continuation passing style (CPS) is an advanced style ofprogramming in which control is passed explicitly in the form ofa continuation:
Every function defined in CPS takes an extra argument,called continuation; whenever the function finishescomputing a value, it calls the continuation function withthe computed value.
This programming style is used often to implement compilersand interpreters for functional programming and logicprogramming.
Continuations. Macros
The continuation passing style
The continuation passing style (CPS) is an advanced style ofprogramming in which control is passed explicitly in the form ofa continuation:
Every function defined in CPS takes an extra argument,called continuation; whenever the function finishescomputing a value, it calls the continuation function withthe computed value.
This programming style is used often to implement compilersand interpreters for functional programming and logicprogramming.
Continuations. Macros
The intuition behind continuations
The evaluation of every expression e happens in a surroundingcontext which represents the computations that is still to be done toproduce a value. This surrounding context is called continuation.Examples of expressions in surrounding contexts:
1 (+ 5 (* 4 3))Context (or continuation) for (* 4 3): (+ 5 •).
I We must replace • with the value of (* 4 3) in thiscontext.
2 (define x 5)(if (= 0 x)
’undefined(remainder (* (+ x 1) (- x 1)) x))
Context (or continuation) for (+ x 1):
(remainder (* • (- x 1)) x))
Continuations. Macros
Being more preciseContinuations as functions
We can form lambda expressions to represent continuations.Examples of continuation expressions in surrounding contexts:
Context C and expression e The continuation of e in C(+ 5 (* 4 3)) (lambda (e) (+ 4 e)(define x 5) (lambda (e)(if (= 0 x) (remainder
’undefined (* e (- x 1) x)))(remainder (* (+ x 1) (- x 1)) x))
Note:The evaluation of e in C is reduced to computing (k v)whereI k is the continuation of e in C.I v is the value of e
Continuations. Macros
Being more preciseContinuations as functions
We can form lambda expressions to represent continuations.Examples of continuation expressions in surrounding contexts:
Context C and expression e The continuation of e in C(+ 5 (* 4 3)) (lambda (e) (+ 4 e)(define x 5) (lambda (e)(if (= 0 x) (remainder
’undefined (* e (- x 1) x)))(remainder (* (+ x 1) (- x 1)) x))
Note:The evaluation of e in C is reduced to computing (k v)whereI k is the continuation of e in C.I v is the value of e
Continuations. Macros
The continuation-passing style (CPS)Main ideas
We want to make continuations manipulableI we extend every function with an additional argument k (its
continuation) that will be applied to the function result.I returning a value is replaced by calling the continuation.
In general, the CPS replaces every definition of a function fwith a definition of a function f-cc, such that:
(f-cc arg1 ... argn k) returns (k v)where v is the value of (f arg1 ... argn)
Continuations. Macros
Illustrated examplesCPS versions of primitive operations
The CPS versions of primitive operations, like addition (+),multiplication (*), and cdr can be defined as follows:
(define (+-cc x y k) (k (+ x y)))(define (*-cc x y k) (k (* x y)))(define (cdr-cc lst k) (k (cdr lst)))
> (+-cc 1 2 list)’(3)> (*-cc 2 3 (lambda (v) (cons v v)))’(6 . 6)
Continuations. Macros
Illustrated examplesSumming the elements of a list
(define (sum lst)(cond [(cons? lst) (+ (car lst) (sum (cdr lst)))]
[(null? lst) 0][#t ’undefined]))
The CPS version of sum is sum-cc defined as follows:
(define (sum-cc lst k)(cond [(cons? lst)
(sum-cc (cdr lst)(lambda (v) (k (+ (car lst) v)))); or (lambda (v) (+-cc (car lst) v k))]
[(null? lst) (k 0)][#t (k ’undefined)]))
> (sum-cc ’(1 2 3) display)6
Continuations. Macros
Summing the elements of a listTracing the computation
(sum-cc ’(1 2 3) display)⇓(sum-cc ’(2 3) (lambda (e) (display (+ 1 e))))⇓(sum-cc ’(3) (lambda (e) ((lambda (e) (display (+ 1 e))) (+ 2 e))))⇓(sum-cc ’() (lambda (e)
((lambda (e)((lambda (e) (display (+ 1 e)))(+ 2 e)))
(+ 3 e))))⇓((lambda (e)
((lambda (e)((lambda (e) (display (+ 1 e)))(+ 2 e)))
(+ 3 e))))0)
⇓6
Continuations. Macros
Remarks
sum-cc is tail-recursiveThe “size” of the continuation increases with eachrecursive callThe evaluation of the base-case of sum-cc istime-consuming.
Continuations. Macros
Continuation-passing-style (CPS)CPS definitions of if and =
(define (if-cc test iftrue iffalse)(if test (iftrue) (iffalse)))
(define (=-cc x y k) (k (= x y)))
Remarksif-cc is the CPS version of if. It has two continuations:I iftrue for the first branchI iffalse for the second branch
Both continuations are nullary functions.
Continuations. Macros
Handling exceptions in CPS
In CPS, exceptions are explicit.
Example (Definition of division in CPS)Division by 0 should raise an exception:
(define (/-cc x y k-ok k-err)(=-cc 0 y (lambda (v)
(if-cc vk-err(lambda () (k-ok (/ x y))))))))
> (/-cc 3 0 display(lambda () (display "div by zero"))
div by zero> (/-cc 3 1 display
(lambda () (display "div by zero"))3
Continuations. Macros
Continuations in RACKET
RACKET has built-in support to work with continuations.It has a primitive that captures a continuation of anexpression e in a context CThe primitive is calledcall-with-current-continuation, or call/cc as ashort aliascall/cc takes a parameter, which is a function of oneparameter.The parameter of the function is bound to the continuation,and the body of the function is e
Continuations. Macros
Capturing continuations with call/ccExamples
> (+ 5 (call/cc (lambda (cont) (* 4 3))))17 ; cont is bound to (lambda (e) (+ 5 e))
> (cons 1 (cons 2 (cons 3 (call/cc (lambda (k) ’()) ))))’(1 2 3) ; k is bound to (lambda (e) (cons 1 (cons 2 (cons 3 e))))
> (define x 5)> (if (= 0 x)
’undefined(remainder (* (call/cc (lambda (k) (+ x 1))) (- x 1)) x))
4 ; k is bound to (lambda (e) (remainder (* e (- x 1)) x)) where x=5
In these examples we did not do anything with the capturedcontinuations:
We can store them in a global variable, and use them later.(See next slide)
Continuations. Macros
Capturing continuations with call/ccExamples
> (+ 5 (call/cc (lambda (cont) (* 4 3))))17 ; cont is bound to (lambda (e) (+ 5 e))
> (cons 1 (cons 2 (cons 3 (call/cc (lambda (k) ’()) ))))’(1 2 3) ; k is bound to (lambda (e) (cons 1 (cons 2 (cons 3 e))))
> (define x 5)> (if (= 0 x)
’undefined(remainder (* (call/cc (lambda (k) (+ x 1))) (- x 1)) x))
4 ; k is bound to (lambda (e) (remainder (* e (- x 1)) x)) where x=5
In these examples we did not do anything with the capturedcontinuations:
We can store them in a global variable, and use them later.(See next slide)
Continuations. Macros
Capturing, storing, and applying continuations
> (define-values (cont-remember x) (values #t 5))> (+ 5 (call/cc (lambda (k)
(set! cont-remember k)(* 4 3))))
17 ; cont-remember is (lambda (e) (+ 5 e))> (cont-remember 3)8> (cons 1 (cons 2 (cons 3
(call/cc (lambda (k)(set! cont-remember k) ’())))))
’(1 2 3) ; cont-remember is (lambda (e) (cons 1 (cons 2 (cons 3 e))))> (cont-remember ’(7 8))’(1 2 3 7 8)> (define x 5)> (if (= 0 x)
’undefined(remainder (* (call/cc (lambda (k)
(set! cont-remember k) (+ x 1)))(- x 1))
x))4 ; cont-remember is (lambda (e) (remainder (* e (- x 1)) x))where x=5> (cont-remember 3)2
Continuations. Macros
Use of continuations for escaping purposes
> (+ 5(call/cc(lambda (k)(* 4 (k 10)))) ) ; returns the value of (k 10)
15> (cons 1
(call/cc(lambda (k)(cons 2(cons3 (k ’x))))) )
’(1 . x)> (define x 5)> (if (= 0 x)
’undefined(call/cc(lambda (k)(remainder(* (+ x 1)
(- x (k 111)))x))))
111
Continuations. Macros
Practical exampleComputing the length of an improper list
We wish to define (list-length lst) which returnsthe length of lst, if lst is a list’improper-list if lst is not a list.
(define (list-length l)(call/cc(lambda (do-exit)(letrec ((list-length1
(lambda (l)(cond ((null? l) 0)
((pair? l) (+ 1 (list-length1 (cdr l))))(else (do-exit ’improper-list))))))
(list-length1 l)))))
Continuations. Macros
Macros
Identifiers are names of meaningful entities. In RACKET, thereare three kinds of identifiers:
1 Identifiers of special forms: cond, lambda, . . .they identify syntactic constructs with predefined rules ofevaluation.
2 Variables these are names assigned to valuesI predefined: pi, sin, +, . . .I defined by the user
3 Macro-identifier
Continuations. Macros
What are macros?
A macro is an expression (macro-id . . .)where macro-id is a macro-identifier.
A macro is like a function call, but:I a function call is computed by the interpreterI a macro is a call of a syntax transformer
macro-id is the name of a syntax transformer.I the syntax transformer is special function which indicates
how to transform a syntactic construct e into anothersyntactic construct e′.
e′ is called the expansion of e
I The expansion of e is computed in the expansion phase, bythe preprocessor (or expander) of RACKET
I Afterwards, the interpreter computes the value of theexpansion of e.
Continuations. Macros
The evaluation of macros
(macro-id . . .)
expansion e′
value of e′
expansion (performed by the expander)
evaluation (performed by the interpreter)
macro-id is either predefined or user-defined.
How to define macros?1 with define-syntax-rule (the easy way).2 with a combination of define-syntax andsyntax-rules (the more complicated way)
Continuations. Macros
The evaluation of macros
(macro-id . . .)
expansion e′
value of e′
expansion (performed by the expander)
evaluation (performed by the interpreter)
macro-id is either predefined or user-defined.
How to define macros?1 with define-syntax-rule (the easy way).2 with a combination of define-syntax andsyntax-rules (the more complicated way)
Continuations. Macros
Macro definitionsDefinitions with define-syntax-rule
Example (Swap the values of two variables)We want to write (swap x y) instead of
(let ([tmp x])(set! x y)(set! y tmp)
We can use define-syntax-rule to define the macro swap:
(define-syntax-rule (swap x y)(let ([tmp x])(set! x y)(set! y tmp)))
The blue part is called the template of the syntax rule.
Continuations. Macros
Macro definitionsDefinitions with define-syntax-rule
Example (Swap the values of two variables)We want to write (swap x y) instead of
(let ([tmp x])(set! x y)(set! y tmp)
We can use define-syntax-rule to define the macro swap:
(define-syntax-rule (swap x y)(let ([tmp x])
(set! x y)(set! y tmp)))
The blue part is called the template of the syntax rule.
Continuations. Macros
Evaluation of macro definitionsExamples
> (define a 1)> (define b 2)> (list a b)’(1 2)> (swap a b) ; same as (let ([tmp a]) (set! a b) (set! b tmp))’(2 1)> (swap a 1) ; same as (let ([tmp a]) (set! a 1) (set! 1 tmp))set!: not an identifier in: 1
Remarks1 A syntax transformer transforms syntactic structures. To
avoid errors, we must make sure we call it with argumentsof the right kind!
Continuations. Macros
Evaluation of macro definitionsExamples
> (define a 1)> (define b 2)> (list a b)’(1 2)> (swap a b) ; same as (let ([tmp a]) (set! a b) (set! b tmp))’(2 1)> (swap a 1) ; same as (let ([tmp a]) (set! a 1) (set! 1 tmp))set!: not an identifier in: 1
Remarks1 A syntax transformer transforms syntactic structures. To
avoid errors, we must make sure we call it with argumentsof the right kind!
Continuations. Macros
Things you should never do
In RACKET, we can define the identifiers of special forms asvariables (you should never do this!)
> (define set! 4)> set! ; set! is now a variable with value 4> (set! a 6) ; we can not use the special form set! anymore because we redefined itapplication: not a procedure; ...
Continuations. Macros
Syntax transformers are clever
> (define tmp 5) (define a 6)> (swap tmp a) ; swap the values of tmp and a> (list tmp a)’(6 5)
It works but ... WHY?
A naive syntax transformer replaces (swap tmp a) with
> (let ([tmp tmp])(set! tmp a)(set! a tmp)))
⇒ we don’t distinguish between variable tmp at expansion time, and tmp atevaluation time: Their names clash! This is not what we want.
The expander is clever! It renames tmp at expansion time to avoid nameclashes:
The expansion of (swap tmp a) is(let ([tmp1 tmp] (set! tmp a) (set! a tmp1)))
Continuations. Macros
Syntax transformers are clever
> (define tmp 5) (define a 6)> (swap tmp a) ; swap the values of tmp and a> (list tmp a)’(6 5)
It works but ... WHY?
A naive syntax transformer replaces (swap tmp a) with
> (let ([tmp tmp])(set! tmp a)(set! a tmp)))
⇒ we don’t distinguish between variable tmp at expansion time, and tmp atevaluation time: Their names clash! This is not what we want.
The expander is clever! It renames tmp at expansion time to avoid nameclashes:
The expansion of (swap tmp a) is(let ([tmp1 tmp] (set! tmp a) (set! a tmp1)))
Continuations. Macros
Syntax transformers are clever
> (define tmp 5) (define a 6)> (swap tmp a) ; swap the values of tmp and a> (list tmp a)’(6 5)
It works but ... WHY?
A naive syntax transformer replaces (swap tmp a) with
> (let ([tmp tmp])(set! tmp a)(set! a tmp)))
⇒ we don’t distinguish between variable tmp at expansion time, and tmp atevaluation time: Their names clash! This is not what we want.
The expander is clever! It renames tmp at expansion time to avoid nameclashes:
The expansion of (swap tmp a) is(let ([tmp1 tmp] (set! tmp a) (set! a tmp1)))
Continuations. Macros
The interpreter is cleverWhat happens if, at evaluation time we use variables whose names clash with theidentifiers of special forms used in the template of the syntax rule?
> (let ([set! 5][a 6])
(swap set! a) ; set! gets value 6 and a gets value 5(list set! a)) ; should return ’(6 5)
> ’(6 5)
it works but ... WHY?
A naive syntax transformer produces(let ([set! 5]
[a 6])(let ([tmp set!])(set! set! a) ←name clash!(set! a tmp)))
(list set! a))
The interpreter avoids name clashes too:(let ([set!1 5] set! was renamed to set!1
[a 6])(let ([tmp set!1])(set! set!1 a)(set! a tmp)))
(list set!1 a))
Continuations. Macros
The interpreter is cleverWhat happens if, at evaluation time we use variables whose names clash with theidentifiers of special forms used in the template of the syntax rule?
> (let ([set! 5][a 6])
(swap set! a) ; set! gets value 6 and a gets value 5(list set! a)) ; should return ’(6 5)
> ’(6 5)
it works but ... WHY?A naive syntax transformer produces(let ([set! 5]
[a 6])(let ([tmp set!])
(set! set! a) ←name clash!(set! a tmp)))
(list set! a))
The interpreter avoids name clashes too:(let ([set!1 5] set! was renamed to set!1
[a 6])(let ([tmp set!1])
(set! set!1 a)(set! a tmp)))
(list set!1 a))
Continuations. Macros
The evaluation of macros
RemarkRACKET always renames the variables to avoid the interferenceof variables with the same name at expansion time and atevaluation time.
Continuations. Macros
Macro definitionsDefinitions with define-syntax and syntax-rules
This method is used to define syntax transformers with syntax rulesfor multiple patterns
(define-syntax macro-id(syntax-rules (literal-id1 ... literal-idm)[pat1 template1]...[patn templaten]))
where every pati must be of the form (macro-id . . .).
Example
(define-syntax prefs(syntax-rules ()[(prefs) null][(prefs a) ’(() (a))][(prefs a b) ’(() (a) (a b))]))
> (prefs (+ 1 2) (+ 3 4)) ; apply third syntax rule’(() ((+ 1 2)) ((+ 1 2) (+ 3 4)))> (prefs 1 2 3) ; no syntax rule us applicableprefs: bad syntax in: ...
Continuations. Macros
Macro definitionsDefinitions with define-syntax and syntax-rules
This method is used to define syntax transformers with syntax rulesfor multiple patterns
(define-syntax macro-id(syntax-rules (literal-id1 ... literal-idm)[pat1 template1]...[patn templaten]))
where every pati must be of the form (macro-id . . .).
Example
(define-syntax prefs(syntax-rules ()[(prefs) null][(prefs a) ’(() (a))][(prefs a b) ’(() (a) (a b))]))
> (prefs (+ 1 2) (+ 3 4)) ; apply third syntax rule’(() ((+ 1 2)) ((+ 1 2) (+ 3 4)))> (prefs 1 2 3) ; no syntax rule us applicableprefs: bad syntax in: ...
Continuations. Macros
Macro patterns with matching sequences
Let’s define the macro (split-last a1 . . . an) which expands to’((a1 . . . an−1) an) if n > 0, and to ’(() ()) if n = 0.
> (define-syntax split-last(syntax-rules ()
[(split-last) ’(() ())][(split-last as ... b) ’((as ...) b)]))
> (split-last)’(() ())> (split-last a)’(() (a))> (split-last 1 (+ 2 3) 3 (/ 6 2))’((1 (+ 2 3) 3) (/ 6 2))
Pattern variables followed by ... match sequences of syntactic expressions.In the previous example:
as matches all arguments of split-last except the last one
b matches the lat argument.
Continuations. Macros
Macro patterns with matching sequences
Let’s define the macro (split-last a1 . . . an) which expands to’((a1 . . . an−1) an) if n > 0, and to ’(() ()) if n = 0.
> (define-syntax split-last(syntax-rules ()
[(split-last) ’(() ())][(split-last as ... b) ’((as ...) b)]))
> (split-last)’(() ())> (split-last a)’(() (a))> (split-last 1 (+ 2 3) 3 (/ 6 2))’((1 (+ 2 3) 3) (/ 6 2))
Pattern variables followed by ... match sequences of syntactic expressions.In the previous example:
as matches all arguments of split-last except the last one
b matches the lat argument.
Continuations. Macros
A more intricate exampleA user-defined macro for and
(and e1 e2 . . . en) is logically equivalent with
(if e1 (if e2 e2 (... (if en−1 en en−1) ...)) e1)
Let’s define (my-and e1 e2 . . . en) to expand into e1 if n = 1,and the equivalent nested if expression otherwise:
(define-syntax my-and(syntax-rules ()
[(my-and a) a][(my-and a b ...) (if a (my-and b ...) a)]))
RemarksThe expansion of (my-and e1 e2 . . . en) may requireseveral expansion steps, because my-and is definedrecursively.Only when all expansions end, will the interpreter start toevaluate the fully expanded expression.
Continuations. Macros
A more intricate exampleA user-defined macro for and
(and e1 e2 . . . en) is logically equivalent with
(if e1 (if e2 e2 (... (if en−1 en en−1) ...)) e1)
Let’s define (my-and e1 e2 . . . en) to expand into e1 if n = 1,and the equivalent nested if expression otherwise:
(define-syntax my-and(syntax-rules ()
[(my-and a) a][(my-and a b ...) (if a (my-and b ...) a)]))
RemarksThe expansion of (my-and e1 e2 . . . en) may requireseveral expansion steps, because my-and is definedrecursively.Only when all expansions end, will the interpreter start toevaluate the fully expanded expression.
Continuations. Macros
A more intricate exampleA user-defined macro for and
(and e1 e2 . . . en) is logically equivalent with
(if e1 (if e2 e2 (... (if en−1 en en−1) ...)) e1)
Let’s define (my-and e1 e2 . . . en) to expand into e1 if n = 1,and the equivalent nested if expression otherwise:
(define-syntax my-and(syntax-rules ()
[(my-and a) a][(my-and a b ...) (if a (my-and b ...) a)]))
RemarksThe expansion of (my-and e1 e2 . . . en) may requireseveral expansion steps, because my-and is definedrecursively.Only when all expansions end, will the interpreter start toevaluate the fully expanded expression.
Continuations. Macros
Macro patterns with matching sequencesA useful use of ... in rule templates
If to and from are macro pattern variables that match sequences of same length,and to is bound to a sequence of identifiers, then
(set! to from) ...
in the template causes the duplication of set! as many times as necessary, to use eachidentifier matched in the to and from sequences.
Example
> (define-syntax-rule (my-set! (xs ...) (vs ...))(local [] (set! xs vs) ... (list xs ...)))
> (define-values (x y z) (values 9 8 7))> (list x y z)’(9 8 7)> ; this macro expands to
; (local [] (set! x 1) (set! y 2) (set! z 3) (list x y z))(my-set! (x y z) (1 2 3))
> (list x y z)’(1 2 3)
Continuations. Macros