; Evaluation of this file yields an HTML document
; $Id: impromptu-shift-tr.scm 632 2005-03-23 23:27:38Z oleg $
(define Content
'(html:begin
(Header
(title "How to remove a dynamic prompt")
(description "Dynamic and static delimited control operators are
equally expressible. Macro-expressing control/prompt etc. in terms of
shift/reset, and formal proofs.")
(Date-Revision-yyyymmdd "20050323")
(Date-Creation-yyyymmdd "20050111")
(keywords "Scheme, control, prompt, shift, reset, delimited
continuation, abstracting control")
(Author "Oleg Kiselyov")
(Affiliation "")
; (Affiliation "Fleet Numerical Meteorology and Oceanography Center,
; Monterey, CA 93943")
(AuthorAddress "oleg-at-okmij.org")
(long-title ("How to remove a dynamic " "prompt: "
"static and dynamic " "delimited continuation operators "
"are equally expressible"))
(Links
(start "index.html" (title "Scheme Hash"))
(contents "../README.html")
(prev "enumerators-callcc.html")
(next "universally-CooL-symbols.html")
(top "index.html")
(home "http://pobox.com/~oleg/ftp/")))
(body
(navbar)
(page-title)
(abstract
"We show how to remove a dynamic " (code "prompt") " (aka " (code
"reset") ") and thus to turn so-called static delimited continuation
operators (" (code "shift") "/" (code "reset") ") into dynamic ones:
" (code "control") ", " (code "control0") ", " (code "shift0") ". Our
technique extends the continuation captured by " (code "shift") " by
composing it with the previous fragments of the `stack'. Composition
of context fragments " (em "can") " be done via regular functional
composition. We thus demonstrate that all the above delimited continuation
operators are " (em "macro") "-expressible in term of each other ---
without capturing undelimited continuations and without using mutable
state. Furthermore, the operators " (code "shift") ",
" (code "control") ", " (code "control0") ", " (code "shift0") " are
the members of a single parameterized family, and the standard CPS is
sufficient to express their denotational semantics." (br)
(indent)
"We give the simplest Scheme implementation of the dynamic
control operators. We give a formal simulation proof that " (code
"control") " realized through " (code "shift") " indeed has its
standard reduction semantics." )
(TOC)
(Section 2 "Introduction")
(p "There have been proposed a number of delimited continuation
operators, which are concisely surveyed in " (cite "shift-to-control")
". The most well-known operators are " (code "control") "/" (code
"prompt") " (which were historically the first) and " (code "shift")
"/" (code "reset") ". It is of considerable interest both to the
implementors and the users to establish the relationship among various
control operators. In one direction, it is obvious that " (code
"shift") "/" (code "reset") " is macro-expressible " (cite
"Felleisen-expressive") " in terms of " (code "control") "/" (code
"prompt") ". Whether " (code "control") "/" (code "prompt") " and
similar operators (" (code "shift0") ", " (code "splitter") ", " (code
"spawn") ", " (code "cupto") ", etc.) can be macro-expressed in terms
of " (code "shift") "/" (code "reset") " has been for a long time an
open question " (cite "Gunter-cupto") ". The so-called static
delimited continuation operators " (code "shift") "/" (code "reset") "
have the well-known denotational (CPS) semantics " (cite "abstracting-control")
". It was unknown if " (code "control") "/" (code "prompt") " and the
other, so-called dynamic delimited continuation operators, have
likewise CPS semantics --- or they require extensions to the standard
CPS, as sometimes claimed " (cite "abstract-continuations") ".")
(p "These open questions were answered by Chung-chieh Shan " (cite
"shift-to-control") ", who for the first time showed that the dynamic
delimited continuation operators are macro-expressible in terms of
" (code "shift") "/" (code "reset") ". This paper presents another,
more uniform solution. We outline the general method of removing a
dynamic " (code "prompt") ", and so reify the operation of composing
frames of control stack. Contrary to the common belief " (cite
"abstract-continuations")
", we do not need any special primitives for composing stack fragments
other than the operation of capturing the fragments with
" (code "shift") "/" (code "reset") ", which is expressible in standard CPS.
Furthermore, we show that both
static and dynamic delimited continuation operators are the members of
a single family of delimited control, parameterized by simple
functions specifying the composition of stack fragments. We
demonstrate the simplest known Scheme implementation of " (code
"control") "/" (code "prompt") " and other delimited control operators
in terms of " (code "shift") "/" (code "reset") ". Finally, we
formally prove that " (code "control") " implemented in terms of
" (code "shift") " indeed satisfies its standard reduction
semantics. Our proof method is general and can be used for proving
other properties of delimited continuations.")
(p "The next section introduces notation and defines delimited
continuation operators under investigation. Section 3 formulates the
theorem of macro-expressivity of " (code "control") "/" (code
"prompt") " in terms of " (code "shift") "/" (code "reset") ", and
gives corollaries. One of them states that the delimited continuation
operators are the members of the same family. We then give the Scheme
implementation of the dynamic delimited continuation operators, and tests. In
Section 5, we prove the macro-expressivity theorem. The proof
demonstrates the composition of captured delimited continuations,
which is achievable without any new primitives. We then conclude.")
(Section 2 "Notation")
(p "For simplicity, we will be considering delimited continuation
operators in the context of untyped call-by-value
lambda-calculus. Generalizations to richer call-by-value calculi are
straightforward. Table " (xref "FigSyntax") " presents the syntax of
the language. We write " ($ (let ((x e1)) e2)) " as an abbreviation for
" ($ ((lambda (x) e2) e1)) ". Table " (xref "FigRed") " gives the
standard small-step operational semantics of the control-free fragment
of the language, by introducing the reduction relation " ($ (red e1
e2)) " and the evaluation context " ($ (M)) ". We write " ($ (red* e1
e2)) " for the composition of zero or more reduction
steps. Intuitively, to reduce a term " ($ e) ", we find its
representation in the form "($ (M e0)) " where " ($ e0) " is a redex
and " ($ (red e0 e1)) ". Then "($ (red (M e0) (M e1))) ". As usual,
"($ (ren (x v) e)) " means a capture-free substitution of value " ($
v) " for variable " ($ x) " in expression " ($ e) ".")
(p "A typical proposal " (cite "shift-to-control") " for treating
delimited continuations as first class values defines syntactic forms
to set the delimiter, and to capture and reify the continuation up to
the delimiter. The delimiter setting-form is commonly called
``prompt'' or ``reset''. We will write " ($ (a-prompt ...)) " for a
general prompt, and " ($ (prompt ...)) ", " ($ (reset ...)) " etc. for
the delimiters of specific delimited continuation proposals. The
capturing and reifying form is commonly called ``shift'' or
``control''. Likewise, we will use the notation " ($ (a-control f
e)) " for a generic form and " ($ (shift f e)) ", " ($ (control f
e)) " etc. for a specific delimited continuation operator.")
(figure FigSyntax
"Syntax"
(table-of-expr
(ebnf-def ($ e) (comment "Expression")
(ebnf-choice
(($ v) (comment "Value"))
(($ ((e1 e2))) (comment "Application"))
(($ (a-prompt e)) (comment "Set a delimiter"))
(($ (a-control f e)) (comment "Capture and reify"))))
(ebnf-def ($ v) (comment "Value")
(ebnf-choice
((ebnf-several ($ x) ($ y) ($ f) ($ g)) (comment "Variables"))
(($ (lambda (x) e)) (comment "Abstraction"))))
))
(figure FigRed
"Operational Semantics"
(table-of-expr
(ebnf-def ($ (C)) (comment "(Delimited) Evaluation context")
(ebnf-choice
(($ Hole) (comment "Hole"))
(($ ((C) e)))
(($ (v (C))) (comment "Application"))))
(ebnf-def ($ (M)) (comment "Arbitrary evaluation context")
(ebnf-choice
($ (C))
(($ (M (a-prompt (C)))) (comment ""))))
(($ (red
((lambda (x) e) v)
(ren (x v) e)))
(comment ("(" (greek beta) "-) Redex")))
))
(p "To account for delimited continuation operators, we augment the
operational semantics in Table 2. The so-called ``static'' delimited
continuation operators " (code "shift") "/" (code "reset") " are
defined by the following reduction rules.")
(peq "S1" ($ (red (M (reset v)) (M v))))
(peq "S2" ($ (red (M (reset (C (shift f e))))
(M (reset e'))))
(br)
"where " ($ (is e' (let ((f (lambda (x) (reset (C x))))) e))))
(pn "Here, " ($ (C)) " is the context that does not cross the
delimiter's (i.e., " (code "reset") ") boundary. In contrast, the
context " ($ (M)) " may do so.")
(p "All these reduction rules do not handle the case when
"($ (a-control f e))
" occurs without a dynamically enclosing " ($ (a-prompt ...)) ". We
shall call a program that exhibits such a case to be ``stuck on
control.'' We could have followed the original Danvy and Filinski's
proposal of assuming that the entire program is enclosed in a
top-level
" ($ (a-prompt ...)) ". However, we chose to consider stuck on control
expressions to be in error --- as do several implementations of
delimited continuations, e.g., Scheme48.")
(p "The most well-known of so-called ``dynamic'' delimited
continuation operators (and historically the first delimited
continuation operators proposed) are " (code "control") "/" (code
"prompt") ":")
(peq "C1" ($ (red (M (prompt v)) (M v))))
(peq "C2" ($ (red (M (prompt (C (control f e))))
(M (prompt e'))))
(br)
"where " ($ (is e' (let ((f (lambda (x) (C x)))) e))))
(p "All various delimited continuation operators " ($ (a-control f e))
" capture the continuation up to the closest dynamically enclosing
delimiter, and reify that continuation as a function. That function,
bound to the variable " ($ f) " in the body " ($ e) " may be invoked
in the body zero or more times. The reified delimited continuation may
also be incorporated in the result of " ($ e) ", and invoked elsewhere
in the program many times. The difference among the delimited
continuation operators emerges when either the body " ($ e) " or the
captured continuation themselves contain delimited continuation
operators. The operator " (code "shift") " encloses the captured
continuation in a delimiter, whereas " (code "control") " does
not. Therefore, in the case of " (code "shift") ", the delimited
continuation operators in " ($ (C)) " can capture only a part of that
context, but no more. In contrast, in the case of " (code "control") ",
delimited continuation operators embedded in " ($ (C)) " can capture
continuations that span far beyond that context. Other such dynamic
delimited continuation operators are possible, whose reduction rules
eliminate even more delimiters:")
(peq "S2'" ($ (red (M (reset0 (C (shift0 f e))))
(M e')))
(br)
"where " ($ (is e' (let ((f (lambda (x) (reset0 (C x))))) e))))
(peq "C2'" ($ (red (M (prompt0 (C (control0 f e))))
(M e')))
(br)
"where " ($ (is e' (let ((f (lambda (x) (C x)))) e))))
(p "Both these operators do not preserve the original delimiter,
permitting therefore the body of the delimited continuation operator
to capture a part of " ($ (M)) ". The operator " (code "shift0") " was
briefly considered in " (cite "abstracting-control-tr") ", whereas " (code
"control0") " is equivalent to a single-prompt " (code "cupto")
" " (cite "Gunter-cupto") ".")
(Section 2 "Removing"
" a dynamic " (code "prompt") " and expressing " (code "control") "
via " (code "shift"))
(p
"We introduce a sum (discriminated union) datatype " (code "H") ", similar to
Haskell's " (code "Either") " datatype:")
(p (ebnf-def ($ H)
(ebnf-choice
($ (H v1 v2))
($ (HV v))))
)
(pn "Such a datatype is trivially realized in our calculus (cf., one of
the Scheme implementations below): constructors " ($ H) " and " ($ HV) "
become regular functions that yield abstractions. Slightly informally,
we will call " ($ (H v1 v2)) " and " ($ (HV v)) " values themselves.")
(p
"We introduce the following source-to-source transformation:")
(peq "PR"
(align-left
($ (expressed-as
(prompt e)
(h (reset (HV e)))))
($ (expressed-as
(control f e)
(shift f' (H (compose h' f') (lambda (f) e))))))
)
(pn "where the functions " ($ "h") " and " ($ "h'") " are defined
as follows:")
(peq "H"
(align-left
($ (is (h (H f x))
(prompt (x f))))
($ (is (h (HV x)) x)))
)
(peq "H'"
(align-left
($ (is (h' (H f x))
(shift g (H (compose h' g f) x))))
($ (is (h' (HV x)) x))))
(p "We also introduce the following transformation rule:")
(peq "PR1"
($ (expressed-as
(M (C e))
(equiv (M (h' (reset (HV (C e)))))
(M (B (C e))))))
)
(pn "The notation " ($ (B e)) " is a mere shorthand for
" ($ (h' (reset (HV e)))) ". The context " ($ (C)) " in (PR1) may be
empty.")
(p "It is straightforward to extend (PR) to expressions and
contexts. We will write " ($ (T n e *e)) " for the transformation of
an expression " ($ e) " yielding " ($ *e) " by applying (PR) and " ($
n) "-times the rule (PR1). When we apply the rule (PR1), the place to
insert " ($ (B)) " is chosen non-deterministically. If the number of
applications of (PR1) is irrelevant, we may write simply " ($ (T e
*e)) ". The properties of the transformation are summarized by the
following Theorem.")
(p
"Theorem 1. " (code "Prompt") " and " (code "control") " are
macro-expressible in terms of " (code "shift") " and " (code "reset")
": applying the transformation (PR) to a program yields an
observationally equivalent program. In particular, the
transformations (PR) and (PR1) have the following properties:")
(peq "T1"
($ *v) " is a value")
(pn "That is, values are transformed to values.")
(peq "T2"
($ (T (M e) (*M *e))))
(pn "Transformations (PR) and (PR1) are context independent. This is
another statement of macro-expressibility.")
(peq "T3"
"if " ($ (red e1 e2)) " and " ($ (T n e1 *e1)) " then "
($ (exists m (T m e2 *e2))) " and " ($ (red* *e1 *e2)))
(pn "That is, the transformed code simulates reductions. In particular:")
(peq "T4"
"if " ($ (red* e v)) " then " ($ (T 0 e *e)) " and " ($ (red* *e *v)))
(pn "and if reductions of " ($ e) " diverge, so do reductions of " ($
*e) ".")
(Section 3 "One family of delimited continuation operators")
(p
"Corollary 1. Both static and dynamic delimited continuation
operators are the members of a single family " ($ (greset hr e)) " and
" ($ (gshift hs f e)) " parameterized by functions " (code "hr") " and
" (code "hs") ":")
(table
(tr
(td
(peq #f
(align-left
($ (is (reset e) (greset hr-stop e)))
($ (is (shift f e) (gshift hs-stop f e)))
))
)
(td
(peq #f
(align-left
($ (is (prompt e) (greset hr-stop e)))
($ (is (control f e) (gshift hs-prop f e)))))
))
(tr
(td
(peq #f
(align-left
($ (is (reset0 e) (greset hr-prop e)))
($ (is (shift0 f e) (gshift hs-stop f e)))))
)
(td
(peq #f
(align-left
($ (is (prompt0 e) (greset hr-prop e)))
($ (is (control0 f e) (gshift hs-prop f e)))))
)))
(p "where " ($ (greset hr e)) " and " ($ (gshift hs f e)) " and the
functions " ($ hr-stop) ", " ($ hs-stop) ", " ($ hr-prop) ", and " ($
hs-prop) " are themselves macro-expressible in terms of " (code
"shift") " and " (code "reset")
":")
(peq #f
(align-left
($ (expressed-as (greset hr e)
(hr (reset (HV e)))))
($ (expressed-as (gshift hs f e)
(shift f' (H (compose hs f') (lambda (f) e)))))
))
(peq #f
(align-left
($ (is (hr-stop (H f x))
(greset hr-stop (x f))))
($ (is (hr-stop (HV x)) x))
($ (is hs-stop hr-stop))))
(peq #f
(align-left
($ (is (hr-prop (H f x)) (x f)))
($ (is (hr-prop (HV x)) x))
))
(peq #f
(align-left
($ (is (hs-prop (H f x))
(shift g (H (compose hs-prop g f) x))))
($ (is (hs-prop (HV x)) x))
))
(Section 3 "Fischer-style" " CPS transform of dynamic delimited
continuation operators")
(p
"Corollary 2. Dynamic delimited continuation operators " (code
"control") "/" (code "prompt") " and the others have a simple
denotational semantics and a Fischer-style " (cite "FischerCPS") " CPS
transform, induced by the CPS transform of " (code "shift") "/" (code
"reset") ".")
(Section 2 "Scheme" " implementation of the dynamic delimited continuation
operators")
(p "We shall treat the sum datatype " (code "H") " as an abstract
datatype with two constructors " (code "H") " and " (code "HV") " and
a deconstructor " (code "case-H") ". The datatype can be realized by
the following pure lambda-terms in our calculus:")
(verbatim
"; Constructors"
"(define (H a b)"
" (lambda (on-h)"
" (lambda (on-hv)"
" ((on-h a) b))))"
""
"(define (HV v)"
" (lambda (on-h)"
" (lambda (on-hv)"
" (on-hv v))))"
""
"; Deconstructor"
"(define-syntax case-H"
" (syntax-rules ()"
" ((case-H e"
" ((f x) on-h)"
" (v on-hv))"
" ((e (lambda (f) (lambda (x) on-h)))"
" (lambda (v) on-hv)))))"
)
(p "Alternatively, we may, potentially more efficiently, implement the
" (code "H") " datatype via Scheme's indiscriminated union:")
(verbatim
"(define H-tag (list 'H-tag))"
""
"; Constructors"
"(define (H a b) (cons H-tag (cons a b)))"
"(define-syntax HV ; just the identity"
" (syntax-rules ()"
" ((HV v) v)))"
""
"; Deconstructor"
"(define-syntax case-H"
" (syntax-rules ()"
" ((case-H e"
" ((f x) on-h)"
" (v on-hv))"
" (let ((val e))"
" (if (and (pair? val) (eq? (car val) H-tag))"
" (let ((f (cadr val)) (x (cddr val))) on-h)"
" (let ((v val)) on-hv))))))"
)
(p "The family of the delimited continuation operators is implemented
by the straightforward transcription into Scheme of the expressions in
Corollary 1.")
(verbatim
"(define-syntax greset"
" (syntax-rules ()"
" ((greset hr e) (hr (reset (HV e))))))"
""
"(define-syntax gshift"
" (syntax-rules ()"
" ((gshift hs f e)"
" (shift f* (H (lambda (x) (hs (f* x))) (lambda (f) e))))))"
""
"(define (hr-stop v)"
" (case-H v"
" ; on-h"
" ((f x) (greset hr-stop (x f)))"
" (v v))) ; on-hv"
""
"(define hs-stop hr-stop)"
""
"(define (hr-prop v)"
" (case-H v"
" ; on-h"
" ((f x) (x f))"
" ; on-hv"
" (v v)))"
""
""
"(define (hs-prop v)"
" (case-H v"
" ; on-h"
" ((f x)"
" (shift g"
" (H (lambda (y) (hs-prop (g (f y)))) x)))"
" ; on-hv"
" (v v)))"
)
(p "Using the operational semantics of " (code "shift") "/" (code
"reset") ", it is easy to see that " ($ (greset hr-stop e)) " is
"($ (reset e)) ", and " ($ (gshift hs-stop f e)) " is itself
"($ (shift f e)) ".")
(p "The operators " (code "prompt") " and " (code "control") ", in
particular, have the following simple implementation:")
(verbatim
"(define-syntax prompt"
" (syntax-rules ()"
" ((prompt e) (greset hr-stop e))))"
""
"(define-syntax control"
" (syntax-rules ()"
" ((control f e) (gshift hs-prop f e))))"
)
(p "It is patently clear then that " (code "control") " and " (code "prompt")
" are indeed " (em "macro") "-expressible in terms of " (code "shift") " and
" (code "reset") ".")
(Section 3 "Tests")
"The tests run on Scheme48, with its built-in implementation of
" (code "shift") " and " (code "reset") "."
(small-verbatim
"(reset (let ((x (shift f (cons 'a (f '()))))) (shift g x)))"
"; ==> '(a)"
"(prompt (let ((x (control f (cons 'a (f '()))))) (control g x)))"
"; ==> '()"
""
"(prompt ((lambda (x) (control l 2)) (control l (+ 1 (l 0)))))"
"; ==> 2"
"(prompt (control f (cons 'a (f '()))))"
"; ==> '(a)"
"(prompt (let ((x (control f (cons 'a (f '())))))"
" (control g (g x))))"
"; ==> '(a)"
"(prompt0 (prompt0 (let ((x (control0 f (cons 'a (f '())))))"
" (control0 g x))))"
"; ==> '()"
"; prompt0 is the same as reset0"
"(prompt0 (cons 'a (prompt0 (shift0 f (shift0 g '())))))"
"; ==> '()"
"(prompt0 (cons 'a (prompt0 (prompt0 (shift0 f (shift0 g '()))))))"
"; ==> '(a)"
""
"; Example by Chung-chieh Shan"
"(prompt (begin (display (control f (begin (f 1) (f 2))))"
" (display (control f (begin (f 3) (f 4))))))"
"; ==> 132342344234442344442344444234444442344444442344444444234...."
)
(Section 2 "Proof" " of Theorem 1")
(p "In this section we formally prove that " (code "control")
"/" (code "prompt") " operators realized in terms of " (code "shift")
"/" (code "reset") ", Theorem 1, satisfy their standard reduction
semantics, eqs. (C1-C2). To be precise, we prove that reductions in
the translated language (" (code "control") "/" (code "prompt") "
expressed via " (code "shift") "/" (code "reset") ") simulate
reductions in the original language. We must stress that our proof
elucidates how the context fragment compositions (implicit in the original
language with " (code "control") "/" (code "prompt") ") become
" (em "explicit") ", as functional compositions, in the translated
language.")
(p "Properties (T1) and (T2) are obvious. Before we prove the
simulation property (T3), we consider the following Lemma. ")
(p "Lemma 1.")
(peq #f
($ (red* (B v) v)))
(p "The proof is straightforward from the definition of " ($ (B e)) " as
" ($ (h' (reset (HV e)))) ", the fact that " ($ (HV v)) " is also a
value, the definition of " ($ h') " in eq. (H'), and eq. (S1). The Lemma
is easily generalized to arbitrary sequences of " ($ (B)) ".")
(p "The notation " ($ (red e1 e2)) " describes three different
reductions: the " (greek beta) "-reduction in Table " (xref "FigRed")
" and control reductions (C1) and (C2). We shall consider all three
reductions in turn.")
(p "In order for the " (greek beta) "-reduction to apply, the
expression " ($ e1) " must have the form " ($ (M ((lambda (x) e1')
v))) ". Thus we have:")
(peq #f
(align-left
($ (equiv e1 (M ((lambda (x) e1') v))))
($ (T n "" (*M ((B (lambda (x) *e1')) (B *v)))))
(explain "Lemma 1")
($ (red* "" (*M ((lambda (x) *e1') *v))))
($ (red "" (*M (ren (x *v) *e1'))))))
(p "On the other hand,")
(peq #f
(align-left
($ (equiv e1 (M ((lambda (x) e1') v))))
($ (red "" (M (ren (x v) e1'))))
($ (T m "" (*M (ren (x *v) *e1'))))
))
(pn "for some " ($ m) ".")
(p "Another possible reduction is (C1). It applies when the expression
in question has the form " ($ (M (prompt v))) ". Therefore,")
(peq #f
(align-left
($ (equiv e1 (M (prompt v))))
($ (T n "" (*M (h (reset (HV (B *v)))))))
(explain "Lemma 1")
($ (red* "" (*M (h (reset (HV *v))))))
(explain "equations (S1) and (H)")
($ (red* "" (*M *v)))
))
(pn "The latter expression is obviously a transform image from
" ($ (M v)) ", which is the result of one-step reduction of " ($ e1)
".")
(p "Lemma 2.")
(peq #f
($ (red* (B (C (shift g (H (compose h' g f') (lambda (f) e)))))
(shift g (H (lambda (x) (h' (g (B (C (f' x))))))
(lambda (f) e)))))
)
(pn "Informally, the translation image of " ($ (control f e)) " can be
lifted out of " ($ (B (C))) " with the latter context fragment added
to the captured continuation. Indeed,")
(peq #f
(align-left
($ (B (C (shift g (H (compose h' g f') (lambda (f) e))))))
(explain "Definition of " ($ (B)))
($ (equiv ""
(h' (reset (HV (C (shift g (H (compose h' g f') (lambda (f) e)))))))))
(explain "(S2), along with the fact " ($ (HV e)) " may be subsumed ")
(explain "as a part of a non-reset-crossing context " ($ (C)))
($ (red ""
(h' (reset (let ((g (lambda (x) (reset (HV (C x))))))
(H (compose h' g f') (lambda (f) e)))))))
(explain ($ (H a b)) " is a value, apply (S1) and then (H')")
($ (red* ""
(let ((g (lambda (x) (reset (HV (C x))))))
(shift g' (H (compose h' g' h' g f') (lambda (f) e))))))
($ (equiv ""
(shift g' (H (lambda (x) (h' (g' (h' (reset (HV (C (f' x))))))))
(lambda (f) e)))))
($ (equiv ""
(shift g' (H (lambda (x) (h' (g' (B (C (f' x))))))
(lambda (f) e)))))
))
(p "The last reduction to consider, (C2), applies when the expression
" ($ e1) " has the form of " ($ (M (prompt (C (control f e)))))
". When we transform that expression, we have to keep in mind that the
transform of a non--delimiter-crossing context " ($ (C)) " may, in
general, be a delimiter-crossing context: a potentially inserted
" ($ (B)) " contains the delimiter, " (code "reset") ". We first consider the
case, however, when the image of the context " ($ (C)) " is a
non--delimiter-crossing context " ($ (*C)) ". We can then write:")
(peq "*"
(align-left
($ (equiv e1 (M (prompt (C (control f e))))))
($ (T n "" (*M (h (reset (HV (*C
(shift f' (H (compose h' f') (lambda (f) *e))))))))))
(explain "(S2), along with the fact " ($ (HV e)) " may be subsumed ")
(explain "as a part of a non-reset-crossing context " ($ (*C)))
($ (red "" (*M (h (reset
(let ((f' (lambda (x) (reset (HV (*C x))))))
(H (compose h' f') (lambda (f) *e))))))))
(explain ($ (H a b)) " is a value, apply (S1) and then (H)")
($ (red* "" (*M (h (reset (HV
(let ((f' (lambda (x) (reset (HV (*C x))))))
((lambda (f) *e) (compose h' f')))))))))
($ (equiv "" (*M (h (reset (HV
(let ((f (lambda (x) (h' (reset (HV (*C x))))))) *e)))))))
(explain "definition of " ($ (B)))
($ (equiv "" (*M (h (reset (HV
(let ((f (lambda (x) (B (*C x))))) *e)))))))
))
(p "On the other hand,")
(peq #f
(align-left
($ (equiv e1 (M (prompt (C (control f e))))))
(explain "apply (C2)")
($ (red "" (M (prompt (let ((f (lambda (x) (C x)))) e)))))
($ (T m "" (*M (h (reset (HV (let ((f (lambda (x) (B (*C x))))) *e)))))))
))
(pn "for some " ($ m) " --- actually, for " ($ (is m "n+1")) ".")
(p "Finally, we consider the general case when the image of the
context " ($ (C)) " does include " ($ (B)) ". We can always
decompose " ($ (C)) " into " ($ (C2 (C1))) " so that ")
(peq "D"
($ (equiv (C) (C2 (C1))))
($ (T n "" (*M2 (B (*C1)))))
)
(pn "where either of the contexts " ($ (C1)) " or " ($ (C2)) " may be
empty and " ($ (T m (C2) (*M2))) " for some " ($ m) " smaller than " ($
n) ". This equation merely expresses the fact of the insertion of
" ($ (B)) ". It follows then:")
(peq "**"
(align-left
($ (equiv e1 (M (prompt (C (control f e))))))
($ (T n "" (*M (h (reset (HV (*M2 (B (*C1
(shift f' (H (compose h' f') (lambda (f) *e))))))))))))
(explain "Lemma 2")
($ (red* "" (*M (h (reset (HV (*M2
(shift f' (H (lambda (x) (h' (f' (B (*C1 x)))))
(lambda (f) *e))))))))))
))
(p "If the context " ($ (*M2)) " does not cross the delimiter boundary
--- that is, contains no " ($ (B)) ", we can denote it as " ($ (*C2)) "
and apply (*), obtaining ")
(peq #f
($ (*M (h (reset (HV
(let ((f (lambda (x) (B (*C2 (B (*C1 x))))))) *e))))))
)
(pn "which is again the image of ")
(peq #f
($ (M (prompt (let ((f (lambda (x) (C x)))) e)))))
(pn "keeping in mind that " ($ (equiv (C e) (C2 (C1 e)))) " by (D). If,
however, " ($ (*M2)) " did contain " ($ (B)) ", we can apply (D)
and (**) one more time. We recall that " ($ (T m (C2) (*M2)))
" with " ($ m) " strictly less than " ($ n) ", thus our procedure must
terminate.")
(p "Property (T4) is a simple consequence of (T3) and the fact " ($ (T
n v (red* e *v))) ", which follows from Lemma 1. We also observe that
the transform of a stuck-on-control expression is itself stuck on
control.")
(Section 2 "Conclusions")
(p "We have introduced a general method of removing a dynamic prompt,
so that delimited continuation operators may capture continuations
beyond that prompt. Contrary to the previously held beliefs, composing
of contexts can be done by the regular functional composition. No
primitives other than the operation of capturing fragments of the
context with " (code "shift") "/" (code "reset") " are
needed. Therefore, the standard CPS is sufficient for giving
denotational semantics of the dynamic delimited continuations, and the
dynamic delimited continuation operators are just as expressible as
" (code "shift") "/" (code "reset") ". We thus confirm the results of
" (cite "shift-to-control") " using a more general framework and
giving the formal proof.")
(p "Our technique of removing a dynamic prompt is rather general and
thus can be used for the design of many other delimited control
operators, which permit capturing the continuation up to an arbitrary
(not necessarily the closest one) enclosing delimiter. It becomes
entirely up to the user which delimiters to skip and how the captured
frames are to be composed or used separately. The essence of our
technique is reifying the control effect (or the absence of it) into
regular values, instances of the datatype " (code "H") ". Therefore,
the user program has the ability to determine which control effects, if
any, had occurred during the evaluation of a piece of code --- and act
accordingly. Our technique is thus reminiscent of ``referring to the
central authority'' approach by Cartwright and Felleisen " (cite
"extensible-denot") ". However, instead of one central authority, we
have bureaucracy --- a sequence of authorities. A particular
authority may either fulfill a request for service, or pass it, after
perhaps modifying it, up the (dynamic) chain of hierarchy.")
(p "Practically, we have shown that both static and dynamic delimited
continuation operators are the members of a single family of delimited
control, parameterized by simple functions specifying the composition
of stack fragments. This gives rise to the simplest Scheme realization
of the delimited control operators in terms of " (code "shift")
"/"(code "reset") ".")
(Acknowledgment "Acknowledgments")
(p
"This paper owes its existence to " (cite "shift-to-control")
". The inspiration of the latter cannot be over-emphasized. I am very
grateful to Chung-chieh Shan for many insightful discussions. I would
like to thank Amr A. Sabry for his encouragement and shepherding, and
Daniel P. Friedman for valuable comments.")
(BibReferences)
(footer)
)))
;(pp Content)
;------------------------------------------------------------------------
; SXML->LaTeX conversion
; Given a string, check to make sure it does not contain characters
; such as '_' or '&' that require encoding. Return either the original
; string, or a list of string fragments with special characters
; replaced by appropriate "escape sequences"
(define string->goodTeX
(make-char-quotator
'((#\# . "\\#") (#\$ . "\\$") (#\% . "\\%") (#\& . "\\&")
(#\~ . "\\textasciitilde{}") (#\_ . "\\_") (#\^ . "\\^")
(#\\ . "$\\backslash$") (#\{ . "\\{") (#\} . "\\}"))))
; Place the 'body' within the LaTeX environment named 'tex-env-name'
; options is a string or a list of strings that specify optional or
; mandatory parameters for the environment
; Return the list of fragments (suitable for SRV:send-reply)
(define (in-tex-env tex-env-name options body)
(list "\\begin{" tex-env-name "}" options nl
body
"\\end{" tex-env-name "}" nl))
(define (>list a) (if (or (pair? a) (null? a)) a (list a)))
; Typesetting an expression in our calculus
; An expression may be `simple', like variable, or
; `complex', like an application.
; A complex expression is usually typeset with parentheses around it;
; A simple expression is typeset without parentheses.
; Here's the complications:
; first of all, some complex-looking expressions like `context applications'
; (C x) are considered simple.
; Furthermore, complex expressions can also be typeset without surrounding
; parentheses, e.g., if such an expression appears at the `top' level,
; or as an argument to a `context application', `reset', etc.
; So, only an expression knows if it is simple or complex.
; Only the context knows if the enclosing parentheses are required or not.
; So, an expression, to properly typeset itself, must be aware of its
; context. Ken: ``give an expression access to its own context.''
;
; Complications:
; (f x) is a function applications and should be typeset as
; "f x" or "(f x)"
; (C x) is a `context application' and should be typeset as
; "C[x]"
; One may think it's easy to see which is which, based on the `head'
; of the expression. Alas, it is more complex, because of possible
; `decorations': we can write (f1 x), which is an abbreviation for
; ((sub f "1") x)
; as well as (C1 x), which is an abbreviation of ((sub C "1") x)
; So, the processing becomes less clear.
; Additional complication: when we typeset "reset0" as
; "\\mathbf{reset_0}" we wish the subscript to be within the decoration.
; OTH, when we typeset (M1 x) as "\\mathcal{M}_1[x]", we really
; need the subscript to be outside (otherwise, the subscript is mangled).
;
; During evaluation, we pass along the env, with the following
; dynamic bindings
; &paren -- boolean flag if the context suggest we need enclosing paren
; &decor -- list of decorations
;
; We could have used another binding: &ctx
; to handle ``macros''. However, we do that in a different way,
; by returning a function.
(define (typeset-exp tag exp)
(define (context-notation letter)
(lambda (env)
(let ((kw (decorate env `("\\mathcal{",letter"}"))))
(h-simple #t
(lambda e
`(,kw "\\!\\left[" ,e "\\right]"))))))
(define init-env
`((Hole . "[\\enspace]")
(hr-stop . "hr_{stop}")
(hs-stop . "hs_{stop}")
(hr-prop . "hr_{prop}")
(hs-prop . "hs_{prop}")
(HV . "\\mathsf{HV}\\,")
(H . "\\mathsf{H}\\,")
(lambda
. ,(lambda (_)
(h-simple #t
(lambda (formal body)
(list "\\lambda " formal "." body)))))
(let
. ,(lambda (_)
(lambda (envp bindings body)
(let ((env (ext-paren envp #f)))
(add-paren envp
(list "\\mathrm{let}\\,"
(handle env (caar bindings)) "\\!=\\!"
(handle env (cadar bindings))
"\\,\\mathrm{in}\\," (handle env body)))))))
; contexts
(C . ,(context-notation "C"))
(M . ,(context-notation "M"))
(B . ,(context-notation "B"))
; Transformation
; (T n e e1) or (T e e1)
(T . ,(lambda (_)
(lambda (env . args)
(let*-values
(((n args)
(if (= 3 (length args)) (values (car args) (cdr args))
(values #f args))))
(apply
(h-simple #f
(lambda (from to)
`(,from
,(if n `("\\stackrel{",n"}{\\rightharpoonup}")
"\\rightharpoonup")
"\\," ,to)))
env args)))))
(a-prompt . ,(lambda (_)
(h-simple #f
(lambda (e) (list "D[" e "]")))))
(prompt
. ,(lambda (env)
(let ((kw (list "\\mathbf{" (decorate env "prompt") "}\\;")))
(h-simple #t
(lambda (e) (list kw e))))))
(reset
. ,(lambda (env)
(let ((kw (list "\\mathbf{" (decorate env "reset") "}\\;")))
(h-simple #t
(lambda (e) (list kw e))))))
(greset
. ,(lambda (env)
(let ((kw
(lambda (hr)
(list "\\mathbf{"
(decorate env
(list "reset_\\mathrm{" hr "}")) "}\\;"))))
(h-simple #t
(lambda (hr e) (list (kw hr) e))))))
(a-control
. ,(lambda (_)
(h-simple #t (lambda (f e) `("\\eta{}" ,f "." ,e )))))
(control
. ,(lambda (env)
(let ((kw (list "\\mathbf{" (decorate env "control") "}\\;")))
(h-simple #t (lambda (f e) (list kw f "." e))))))
(shift
. ,(lambda (env)
(let ((kw (list "\\mathbf{" (decorate env "shift") "}\\;")))
(h-simple #t (lambda (f e) (list kw f "." e))))))
(gshift
. ,(lambda (env)
(let ((kw
(lambda (hs)
(list "\\mathbf{"
(decorate env
(list "shift_\\mathrm{" hs "}")) "}\\;"))))
(h-simple #t
(lambda (hs f e) (list (kw hs) f "." e))))))
(red
. ,(lambda (_)
(h-simple #f
(lambda (from to)
`(,from "\\,\\vartriangleright\\," ,to)))))
(red*
. ,(lambda (_)
(h-simple #f
(lambda (from to)
`(,from "\\,\\vartriangleright^{*}\\," ,to)))))
(is
. ,(lambda (_)
(h-simple #f
(lambda (from to)
`(,from "\\,=\\;\\," ,to)))))
(then
. ,(lambda (_)
(h-simple #f
(lambda (expr)
`("\\;\\Rightarrow\\;" ,expr)))))
(expressed-as
. ,(lambda (_)
(h-simple #f
(lambda (from to)
`(,from "\\,\\Longrightarrow\\," ,to)))))
(equiv
. ,(lambda (_)
(h-simple #f
(lambda (from to)
`(,from "\\,\\equiv\\," ,to)))))
(ren
. ,(lambda (_)
(lambda (env binding exp)
(let ((env (ext-paren env #f)))
(list (handle env exp)
"["
(handle env (cadr binding))
"/" (handle env (car binding)) "]")))))
(compose
. ,(lambda (_)
(h-simple #t (lambda elems
(list-intersperse elems "\\,\\cdot\\,")))))
(exists
. ,(lambda (_)
(h-simple #t
(lambda (var term)
`("\\exists " ,var ".\\:" ,term)))))
))
; handle an atomic expression
(define (handle-atom env atom)
(if (not (symbol? atom)) atom
(let ((resolved (lookup-def atom env #f)))
(if resolved
(if (procedure? resolved) (handle env (resolved env))
(decorate env resolved))
; check for embdedded decorations
(let* ((str (symbol->string atom))
(len (string-length str)))
(cond
((zero? len) (decorate env str)) ; empty string
((char-numeric? (string-ref str (dec len)))
(handle-atom (ext-decoration env
`(>right "_{" ,(string-ref str (dec len))
"}"))
(string->symbol (substring str 0 (dec len)))))
((char=? #\' (string-ref str (dec len)))
(handle-atom (ext-decoration env '(>right "'"))
(string->symbol (substring str 0 (dec len)))))
((char=? #\* (string-ref str 0)) ; transformation marker
(handle-atom (ext-decoration env '(>in "\\overline"))
(string->symbol (substring str 1 len))))
(else
(decorate env str))))))))
(define (ext-decoration env decoration)
(cons
(cons '&decor
(cons decoration
(cond ((assq '&decor env) => cdr) (else '()))))
env))
(define (ext-paren env flag)
(cons (cons '&paren flag) env))
(define (add-paren env exp)
(if (lookup-def '&paren env #f)
(list "(" exp ")")
exp))
(define (decorate env x)
(let loop ((decor (cond ((assq '&decor env) => cdr) (else '())))
(res x))
(if (not (pair? decor)) res
(let* ((decor1 (car decor)) ; must be (kwd args)
(kwd (car decor1))
(args (cdr decor1)))
(loop (cdr decor)
(case kwd
((>right) (cons res args))
((>in) (list args "{" res "}"))
(else (error "invalid decoration: " decor1))))))))
; handle (a b ...) where 'a' may be a `macro'
(define (handle-app envp app)
(let ((env (ext-paren envp #t))) ; use paren
(let ((hd (handle env (car app))))
(if (procedure? hd)
(apply hd envp (cdr app))
(add-paren envp
(cons hd (map (lambda (x) (handle env x)) (cdr app))))))))
(define (handle env exp)
(if (pair? exp) (handle-app env exp) (handle-atom env exp)))
; For use within init-env
; Produce a complex-result? expression whose constituents are `simple'
; (that is, unparenthesized expressions)
(define (h-simple complex-result? handler)
(lambda (envp . args)
(let ((env (ext-paren envp #f)))
(add-paren (if complex-result? envp env)
(apply handler (map (lambda (x) (handle env x)) args))))))
; get the ball rolling
(let ((res (handle init-env exp)))
(assert (not (procedure? res)))
(cerr res nl)
(list "$" res "$"))
)
(define (generate-TEX Content)
(SRV:send-reply
(pre-post-order Content
`(
; General conversion rules
(@
((*default* ; local override for attributes
. ,(lambda (attr-key . value) (cons attr-key value))))
. ,(lambda (trigger . value) (list '@ value)))
(*default* . ,(lambda (tag . elems)
(cerr "Tag: " tag " ignored" nl)
'()))
(*text* . ,(lambda (trigger str)
(if (string? str) (string->goodTeX str) str)))
(n_ ; a non-breaking space
. ,(lambda (tag . elems)
(list "~" elems)))
(html:begin
. ,(lambda (tag . elems)
(list
"% SXML->TEX created this file" nl
"\\documentclass[english,12pt]{article}" nl
"\\usepackage{babel}" nl
"\\usepackage{amssymb}" nl
"\\usepackage{psfig}" nl
;"\\let\\citeauthoryear\\relax" nl
;"\\let\\iint\\relax" nl
;"\\let\\iiint\\relax" nl
;"\\let\\bibhang\\relax" nl
"\\usepackage[numbers,sort&compress]{natbib1}" nl
;"\\makeatletter \\let\\@biblabel\\@empty \\makeatother" nl
"\\newcommand\\bibfont{\\footnotesize}" nl
"\\providecommand\\newblock{}" nl
;"\\renewcommand\\bibnumfmt[1]{\\releft#1\\redot\\reright}" nl
"\\setlength{\\bibsep}{0pt}" nl
;"\\usepackage{graphicx}" nl
;"\\usepackage[latin1]{inputenc}" nl
;"\\usepackage{times}" nl
;"\\pagestyle{empty}" nl
"\\makeatletter" nl
"%%%%%%%%%%%%%%%%%%%%%%%%%% Textclass specific LaTeX commands." nl
; " \\LyXsNoCenterfalse"
; " \\newcommand{\\lyxendslide}[1]{"
; " \\ifLyXsNoCenter%"
; " \\vfill%"
; " \\fi%"
; " \\ifcase \\value{slidetype}%"
; " \\or % no action for 0"
; " \\end{slide} \\or%"
; " \\end{overlay} \\or%"
; " \\end{note}%"
; " \\fi%"
; " \\setcounter{slidetype}{0}"
; "\\visible"
; " }"
" \\newenvironment{lyxcode}" nl
" {\\begin{list}{}{" nl
; " \\setlength{\\rightmargin}{\\leftmargin}" nl
" \\raggedright" nl
" \\setlength{\\itemsep}{-5pt}" nl
" \\setlength{\\parsep}{-3pt}" nl
" \\normalfont\\ttfamily}%" nl
" \\item[]}" nl
" {\\end{list}}" nl
; " % Uncomment to print out only slides and overlays"
; " %"
; " %\\onlyslides{\\slides}"
; " "
; " % Uncomment to print out only notes"
; " %"
; " %\\onlynotes{\\notes}"
"\\makeatother" nl
"\\sloppy" nl
"\\newcommand{\\minitab}[2][l]{\\begin{tabular}{#1}#2\\end{tabular}}" nl
nl
elems
)))
(Header ; It is just the placeholder for the meta-information
*preorder*
. ,(lambda (tag . headers) '()))
(body
. ,(lambda (tag . elems)
(in-tex-env "document" '() elems)))
(page-title
. ,(lambda (tag)
(let* ((header-parms (find-Header Content))
(title (lookup-def 'long-title header-parms #f))
(authors (lookup-def 'Author header-parms #f))
(affiliations
(lookup-def 'Affiliation header-parms #f))
(emails
(lookup-def 'AuthorAddress header-parms #f)))
(list
"\\thispagestyle{empty}" nl
"\\setcounter{page}{0}" nl
(in-tex-env "center" '()
(list
"\\Large" nl
(in-tex-env "picture" '()
(list
"(0,0)" nl
"\\put(0,54){\\makebox[0pt]{\\sc Technical Report No. 611}}" nl
))
nl "\\vspace*{\\fill}" nl nl
"\\huge" nl
(map (lambda (str)
(list "{\\textbf{" str "}}\\\\" nl))
(>list title)) nl
nl "\\vspace*{\\baselineskip}"nl nl
"\\large by " "\\\\"
"\\Large{}"
"\\vspace*{\\baselineskip}" nl
(list-intersperse (>list authors) "\\\\")
"$^*$"
nl nl
"\\vspace*{\\baselineskip}" nl
"March 2005" nl nl
"\\vspace*{\\fill}" nl nl
"\\Large" nl
"\\makebox[0pt]{\\psfig{figure=IUseal.ps,width=2in}}" nl nl ; "\\\\" nl
(in-tex-env "picture" '()
(list "(0,0)" nl
"\\put(0,-18){\\makebox[0pt]{\\sc Computer Science Department}}" nl
"\\put(0,-36){\\makebox[0pt]{\\sc Indiana University}}" nl
"\\put(0,-54){\\makebox[0pt]{\\sc Bloomington, Indiana 47405-7104}}" nl
))
))
(in-tex-env "picture" '()
(list "(0,0)" nl
"\\put(180,-60){\\makebox[0pt]{\\small{}$^*$Collaborating with Profs. Daniel P. Friedman
and Amr A. Sabry on logical programming systems}}"))
"\\newpage" nl))))
; Standard typography
(em
. ,(lambda (tag . elems)
(list "\\emph{" elems "}")))
(p
. ,(lambda (tag . elems)
(list elems nl nl)))
(pn
. ,(lambda (tag . elems)
(list "\\noindent{}" elems nl nl)))
(div
. ,(lambda (tag . elems)
(in-tex-env "trivlist" '() (list "\\item{}" elems))))
(br
. ,(lambda (tag)
(list "\\\\ ")))
(indent
. ,(lambda (tag) "\\indent{}"))
($ *preorder* . ,typeset-exp)
(code
. ,(lambda (tag . elems)
;(in-tex-env "ttfamily" '() elems) ;
(list "\\texttt{" elems "}")
))
(cite
. ,(lambda (tag key)
(list "\\cite{" key "}")))
(xref
. ,(lambda (tag key)
(list "\\ref{" key "}")))
; (peq
; . ,(lambda (tag eqn . body)
; `("\\parbox{4.2in}{" ,body "\\hfill{}(" ,eqn ")" "}" ,nl ,nl)))
; (peq
; . ,(lambda (tag eqn . body)
; (list "\\par\\noindent{}" (in-tex-env "tabular" '("{cr}")
; (list "\\parbox{4.0in}{\\centering{}\\parbox{4.0in}{" body
; "}} & " (and eqn (list " (" eqn ")")) "\\\\\n")) nl)))
(peq
((explain *macro* . ,(lambda (tag . strs) `("{ " ,strs " }")))
)
. ,(lambda (tag eqn . body)
(list "\\addvspace{\\smallskipamount}"
"\\par\\noindent{}" (in-tex-env "tabular" '("{cr}")
(list "\\parbox{4.4in}{\\centering{}" body
"} & " (and eqn (list " (" eqn ")")) "\\\\")) nl
"\\addvspace{\\smallskipamount}")))
(align-left
. ,(lambda (tag . lines)
(list
"\\parbox[t]{8in}{"
"\\vspace{-\\smallskipamount}"
(in-tex-env "tabbing" '()
(list-intersperse lines "\\\\"))
"\\vspace{-\\medskipamount}"
"}")))
(footnote
. ,(lambda (tag . exp) (list "\\footnote{" exp "}")))
(ul ; Unnumbered lists
. ,(lambda (tag . elems)
(in-tex-env "itemize" '() elems)))
(ol ; Numbered lists
. ,(lambda (tag . elems)
(in-tex-env "enumerate" '() elems)))
(li
. ,(lambda (tag . elems)
(list "\\item " elems nl)))
(dl ; Definition list
; deal with the sequences of dt/dd in CPS
; dl and dt are translated to procedures that take one argument:
; previously set label: list of fragments or #f if none
; The procedure returns a pair: (new-label . generate-fragments)
; Initially, label is #f
((dt ; The item title
. ,(lambda (tag . elems)
(lambda (label)
(cons elems ; elems become the new label
(if label ; the label was set: we've seen dt without dd
(list "\\item [" label "]" nl) ; empty body
'())))))
(dd ; The item body
. ,(lambda (tag . elems)
(lambda (label)
(cons #f ; consume the existing label
(list "\\item [" (or label "") "] " elems nl)))))
)
. ,(lambda (tag . procs) ; execute provs generated by dt/dd
(let loop ((procs procs) (label #f) (accum '()))
(if (null? procs) (in-tex-env "description" '() (reverse accum))
(let ((result ((car procs) label)))
(loop (cdr procs) (car result) (cons (cdr result) accum))))))
)
(URL
. ,(lambda (tag url) (list " \\texttt{" url "} ")))
(LaTeX
. ,(lambda (tag) "\\LaTeX{} "))
(blockquote
. ,(lambda (tag . elems)
(in-tex-env "quote" '() elems)))
(Section ; (Section level "content ...")
. ,(lambda (tag level head-word . elems)
(list #\\
(case level
((1 2) "section")
((3) "subsection")
((4) "subsubsection")
(else (error "unsupported section level: " level)))
"{" head-word elems "}" nl)))
(abstract ; The abstract of the document
((Revision
. ,(lambda (tag) ; Find the Header in the Content
; and create the revision record
(let ((header-parms (find-Header Content)))
(list "The present article specifies revision "
(lookup-def 'Revision header-parms #f) " of SXML. "))))
(prod-note
. ,(lambda (tag . master-url)
(list
"\\\\The paper is written in SXML itself. The present paper is the result of translating that SXML code into \\LaTeX, using an appropriate \"stylesheet\". A different stylesheet converted the specification to HTML. The master file, the stylesheets and the corresponding HTML and \\LaTeX documents are available at " master-url ".")))
(keywords
. ,(lambda (tag)
(let ((header-parms (find-Header Content)))
(list "\\\\Keywords: "
(lookup-def 'keywords header-parms #f) "."))))
)
. ,(lambda (tag . abstract-body)
(in-tex-env "abstract" '() abstract-body))
)
(Acknowledgment
. ,(lambda (tag title)
(list "\\subsection*{" title "}" nl)))
; (References ; (References bibitem ...)
; *preorder* ; (bibitem label key . text)
; . ,(lambda (tag . bib-items)
; (let
; ((widest-label ; first search for the widest label
; ; among the bibitems
; (let loop ((items bib-items) (widest ""))
; (if (null? items) widest
; (loop (cdr items)
; (let ((label (cadar items)))
; (if (> (string-length label)
; (string-length widest))
; label widest))))))
; (processed-items
; (map
; (lambda (bib-item)
; (apply
; (lambda (tag label key . text)
; (list "\\bibitem[" label "]{" key "} " text nl))
; bib-item))
; bib-items)))
; (in-tex-env "thebibliography" (list "{" "" "}")
; processed-items))))
(References ; (References bibitem ...)
((bibitem
. ,(lambda (tag label key . text)
(list "\\bibitem{" key "} " text nl)))
)
. ,(lambda (tag . bib-items)
(in-tex-env "thebibliography" "{}"
bib-items)))
(BibReferences ; (BibReferences bibitem ...)
. ,(lambda (tag)
(list "\\bibliographystyle{mcbride}" nl
"%\\divide\\bibsep 3" nl
"%\\setlength{\\bibsep}{0pt}" nl
"\\bibliography{mybib}" nl
"%\\newcommand{\\bibpreamble}{\\nobreak\\vspace*{-.5\\parskip}}"
nl
"\\kern-\\prevdepth" nl
)))
; (verbatim ; set off pieces of code: one or several lines
; ((*text* . ; Different quotation rules apply within a verbatim block
; ,(let ((string->goodTeX-in-verbatim
; (make-char-quotator
; '( (#\^ . "\\^")
; ))))
; (lambda (trigger str)
; (if (string? str) (string->goodTeX-in-verbatim str) str)))
; ))
; . ,(lambda (tag . lines)
; (in-tex-env "verbatim" '()
; (map (lambda (line) (list " " line nl))
(verbatim ; set off pieces of code: one or several lines
((*text* . ; Different quotation rules apply within a "verbatim" block
,(let ((string->goodTeX-in-verbatim
(make-char-quotator
'((#\space "~") ; All spaces are "hard"
(#\# . "\\#") (#\$ . "\\$") (#\% . "\\%")
(#\& . "\\&") (#\~ . "\\textasciitilde{}")
(#\_ . "\\_") (#\^ . "\\^")
(#\\ . "$\\backslash$") (#\{ . "\\{")
(#\} . "\\}")))))
(lambda (trigger str)
(if (string? str) (string->goodTeX-in-verbatim str) str)))
)
(strong
. ,(lambda (tag . elems)
(list "\\textrm{\\small\\bfseries{}" elems "}")))
)
. ,(lambda (tag . lines)
(in-tex-env "lyxcode" '()
(map (lambda (line)
(list (if (equal? line "") "~" line)
"\\\\" nl))
lines))))
(small-verbatim ; set off pieces of code: one or several lines
((*text* . ; Different quotation rules apply within a "verbatim" block
,(let ((string->goodTeX-in-verbatim
(make-char-quotator
'((#\space "~") ; All spaces are "hard"
(#\# . "\\#") (#\$ . "\\$") (#\% . "\\%")
(#\& . "\\&") (#\~ . "\\textasciitilde{}")
(#\_ . "\\_") (#\^ . "\\^")
(#\\ . "$\\backslash$") (#\{ . "\\{")
(#\} . "\\}")))))
(lambda (trigger str)
(if (string? str) (string->goodTeX-in-verbatim str) str)))
))
. ,(lambda (tag . lines)
(in-tex-env "list" '()
(list "{}"
"{%\\raggedright" nl
"\\setlength{\\rightmargin}{-15pt}" nl
"\\setlength{\\itemsep}{-12pt}" nl
"\\setlength{\\parsep}{-4pt}" nl
"\\small\\ttfamily}%" nl
(map (lambda (line)
(list "\\item "
(if (equal? line "") "~" line)
"\\\\" nl))
lines)))))
(table
; verbatim mode does not work in tabular ...
; we have to emulate
((verbatim
((*text* . ; Different quotation rules apply within a "verbatim" block
,(let ((string->goodTeX-in-verbatim
(make-char-quotator
'((#\space "~") ; All spaces are "hard"
(#\# . "\\#") (#\$ . "\\$") (#\% . "\\%")
(#\& . "\\&") (#\~ . "\\textasciitilde{}")
(#\_ . "\\_") (#\^ . "\\^")
(#\\ . "$\\backslash$") (#\{ . "\\{")
(#\} . "\\}")))))
(lambda (trigger str)
(if (string? str) (string->goodTeX-in-verbatim str) str)))
))
. ,(lambda (tag . lines)
(map (lambda (line) (list "\\texttt{\\small " line "}\\\\"))
lines)))
(peq . ,(lambda (tag eqn . body) body))
(tr ; elems ::= [(@ attrib ...)] td ...
; we disregard all attributes of a row
; The result is (td ...)
. ,(lambda (tag . elems)
(if (and (pair? elems) (pair? (car elems))
(eq? '@ (caar elems)))
(cdr elems)
elems)))
(td ; elems ::= [(@ attrib ...)] body ...
; we're only interested in (align "alignment") attr
; and the (colspan "number") attr
; The result is ("alignment" colspan body ...)
; where "alignment" will be #\l, #\c, #\r
; (#\l if not given); colspan is the integer
. ,(lambda (tag . elems)
(define (get-alignment attrs)
(cond
((assq 'align attrs) =>
(lambda (attr)
;(cerr "align attr: " attr nl)
(cond
((string-ci=? (cadr attr) "left") #\l)
((string-ci=? (cadr attr) "right") #\r)
((string-ci=? (cadr attr) "center") #\c)
(else (error "wrong alignment attribute: " attr)))))
(else #\l)))
(define (get-colspan attrs)
(cond
((assq 'colspan attrs) =>
(lambda (attr)
(let ((val (string->number (cadr attr))))
(assert val)
val)))
(else 1)))
(if (and (pair? elems) (pair? (car elems))
(eq? '@ (caar elems)))
(cons (get-alignment (cadar elems))
(cons (get-colspan (cadar elems))
(cdr elems)))
(cons (get-alignment '())
(cons (get-colspan '())
elems)))))
(th ; elems ::= [(@ attrib ...)] body ...
; we're only interested in (align "alignment") attr
; and the (colspan "number") attr
; The result is ("alignment" colspan body ...)
; where "alignment" will be #\l, #\c, #\r
; (#\c if not given); colspan is the integer
. ,(lambda (tag . elems)
(define (get-alignment attrs)
(cond
((assq 'align attrs) =>
(lambda (attr)
;(cerr "align attr: " attr nl)
(cond
((string-ci=? (cadr attr) "left") #\l)
((string-ci=? (cadr attr) "right") #\r)
((string-ci=? (cadr attr) "center") #\c)
(else (error "wrong alignment attribute: " attr)))))
(else #\c)))
(define (get-colspan attrs)
(cond
((assq 'colspan attrs) =>
(lambda (attr)
(let ((val (string->number (cadr attr))))
(assert val)
val)))
(else 1)))
(if (and (pair? elems) (pair? (car elems))
(eq? '@ (caar elems)))
(cons (get-alignment (cadar elems))
(cons (get-colspan (cadar elems))
(cdr elems)))
(cons (get-alignment '())
(cons (get-colspan '())
elems)))))
)
; (table [(@ attrib ...)] tr ...
. ,(lambda (tag row . rows)
(let*-values
(((attrs rows)
(if (and (pair? row) (eq? '@ (car row)))
(values (cadr row) rows)
(values '() (cons row rows))))
((border?)
(cond
((assq 'border attrs) =>
(lambda (border-attr) (not (equal? "0" (cadr border-attr)))))
(else #f)))
((caption label table-type table-alignment)
(apply values
(map (lambda (name)
(cond
((assq name attrs) => cadr)
(else #f)))
'(caption key table-type align))))
(dummy (assert (pair? rows))) ; at least one row must be given
((ncols) (length (car rows)))
((tex-cols)
(let ((col-codes
(map (lambda (_) (if border? "l|" "l")) (car rows))))
(if border?
(apply string-append
(cons "|" col-codes))
(apply string-append col-codes))))
)
(list
(list
(and (equal? table-alignment "center")
"\\centering")
(in-tex-env "tabular"
(list "{" ; "@{\\extracolsep{-25pt}}"
tex-cols "}")
(list (and border? "\\hline\n")
(map
(lambda (row)
(list
(list-intersperse
(map
(lambda (col)
(apply
(lambda (alignment span . data)
(list "\\multicolumn{" span "}{" alignment "}{"
"\\minitab[" alignment "]{"
data "}}")
)
col))
row)
" & ")
"\\\\" (and border? "\\hline") nl))
rows)
nl))
; (and caption (list "\\caption{"
; (and label
; (list "\\label{" label "}"))
; caption "}"))
))
)))
; Floating figure
(figure
. ,(lambda (tag refkey caption . body)
(in-tex-env "figure" '()
(list body
"\\caption{" (and refkey
(list "\\label{" refkey "}"))
caption "}")
)))
(fbox ; Frame box
. ,(lambda (tag . text)
(list "\\fbox{" text "}")))
; Simple LaTeX drawing
(picture
((textbox .
,(lambda (tag x y . text-strs)
(list "\\put(" x "," y "){" text-strs "} ")))
(line .
,(lambda (tag x y length slope-x slope-y)
(list "\\put(" x "," y "){\\line(" slope-x "," slope-y"){"
length "}} ")))
)
. ,(lambda (tag width height . elems)
(list "\\begin{picture}(" width "," height ")(0,0)" nl
elems
"\\end{picture}" nl))
)
; Include an EPSF file
; A graphicx package is assumes
; Attributes are passed as name=value pairs of the includegraphics command
(includegraphics ; Include an EPSF file
((@ ((*default* . ,(lambda args args))) . ,(lambda args args)))
. ,(lambda (tag maybe-attrs . text)
(let*
((attrs (and (pair? maybe-attrs) (eq? '@ (car maybe-attrs))
(cdr maybe-attrs)))
(text (if attrs text (cons maybe-attrs text))))
(list "\\includegraphics["
(list-intersperse
(map
(lambda (name-val)
(list (car name-val) "=" (cdr name-val)))
attrs)
",")
"]{" text "}"))))
; Fancy characters
(Longrightarrow
. ,(lambda (tag)
"$\\Longrightarrow$"))
(rightarrow
. ,(lambda (tag)
"$\\rightarrow$"))
(diag-arrow ; direction is: nw ne se sw
*preorder*
. ,(lambda (tag direction)
"$\\"
(case direction
((nw) "nw")
((ne) "ne")
((se) "se")
((sw) "sw")
(else (error "wrong direction: " direction)))
"arrow$"))
(symbol-equiv
. ,(lambda (tag)
"$\\equiv$"))
(greek
. ,(lambda (tag letter)
(list "$\\" letter "$")))
(displaymath
((symbol-equiv . ,(lambda (tag) "\\equiv{}"))
(underbrace . ,(lambda (tag text . label)
(list "\\underbrace{" text "}"
(and (pair? label) (list "_{" label "}")))))
(*text*
. ,(lambda (tag str)
(with-output-to-string
(lambda ()
(with-input-from-string str
(lambda ()
(do ((c (read-char) (read-char)))
((eof-object? c))
(case c
((#\space) (display "\\;"))
(else (write-char c))))))))))
)
. ,(lambda (tag . elems)
(list "\\[" elems "\\]" nl)))
; Slide terms
; Centered paragraph
(pcenter
. ,(lambda (tag . elems)
(in-tex-env "center" '() elems)))
(small
. ,(lambda (tag . elems)
(list "{\\small{}" elems "}")))
(strong
. ,(lambda (tag . elems)
(list "{\\rmfamily\\bfseries{}" elems "}")))
(scshape
. ,(lambda (tag . elems)
(in-tex-env "scshape" '() elems)))
(vspace ; in points
. ,(lambda (tag size)
(list "\\vspace{" size "pt" "}")))
(tex ; raw tex expression
*preorder*
. ,(lambda (tag str) str))
(extra ; additional stuff
. ,(lambda (tag . elems)
(list "\\parbox{6in}{\\medskip\\tiny{}" elems "}\\medskip" nl)))
; Grammatical terms
(table-of-expr
((comment *macro*
. ,(lambda (tag str)
`((tex " &") ,str (br))))
)
. ,(lambda (tag . exprs)
(in-tex-env "tabular*"
'("{5in}" "{" "l@{\\extracolsep{1in}}l" "}")
(list-intersperse
exprs
"~&~\\\\\n"))))
(ebnf-def ; Choice of terms
*macro*
. ,(lambda (tag term-def . others)
`(,term-def (tex "$\\:::=\\:$") ,@others)))
(ebnf-choice ; Choice of terms
. ,(lambda (tag . terms)
(list "~~~" (list-intersperse terms "$\\,\\bigm\\vert\\,$"))))
(ebnf-several
. ,(lambda (tag . elems)
(list-intersperse elems ", ")))
)))
)
(generate-TEX Content)
; LocalWords: CPS args env cc expr greset gshift greek redex pcenter Plotkin
; LocalWords: fixpoint Felleisen cupto Chung chieh reifying peq Danvy HV equiv
; LocalWords: Filinski's PR deconstructor hv hr ren ebnf Sabry