Using a continuation twice and its implications for

0 downloads 0 Views 290KB Size Report
Mariangiola Dezani-Ciancaglini, Jerzy Tiuryn, and Pawe l Urzyczyn. Discrimination by ... Richard Kelsey, William Clinger, and Jonathan Rees, editors. Revised5 ...
c

, , 1{28 () Kluwer Academic Publishers, Boston. Manufactured in The Netherlands.

Using a continuation twice and its implications for the expressive power of call/cc HAYO THIELECKE Department of Computer Science Queen Mary and West eld College University of London1

[email protected]

Abstract. We study the implications for the expressive power of call/cc of upward continuations, speci cally the idiom of using a continuation twice. Although such control e ects were known to Landin and Reynolds when they invented J and escape, the forebears of call/cc, they still act as a conceptual pitfall for some attempts to reason about continuations. We use this idiom to refute some recent conjectures about equivalences in a language with continuations, but no other e ects. This shows that rst-class continuations as given by call/cc have greater expressive power than one would expect from goto or exits. Keywords: call/cc, continuations, upward continuations, expressiveness, program equivalence.

1. Introduction You can enter a room once, and yet leave it twice. (Peter Landin) A common informal explanation of continuations is the comparison with forward goto. This is in some sense a very apt simile: forward gotos obviously do not give rise to loops, and continuations, without some re exive type, do not add this ability either | unlike backward gotos, or ml exceptions with their general [21] type [19]. Pushed too far, the goto analogy can be harmful. It is intuitively obvious that if all jumps are forward, one cannot jump to the same label twice. But it would be unsafe to conclude that, by analogy, one cannot invoke the same continuation twice. It was realised by Landin [17] (reprinted as a journal paper [18]) that his J (for jump) operator allows control quite unlike a forward goto. We may however, observe that, when J is added, the function-producing feature amounts roughly to the possibility of jumping to a label after leaving the block in which it was introduced. Similarly, Reynolds writes about his escape: For example, it is possible that the evaluation of the body of an escape expression may not cause the application of the escape function, but may produce the escape function (or some function that may call the escape function) as its value. [24]

2

In a more LISP-inspired jargon, \caus[ing] the application of the escape function" is called a downward use of the captured continuations, whereas \ the escape function (or some function that may call the escape function)" produced as the value is an upward continuation [9]. The rst idiom of upward continuations that Reynolds mentions is escape k in k, or written with call/cc, (call/cc (lambda (k) k)). This implies a selfapplication of the continuation. With another self-application, one can de ne recursive functions, as in this obfuscated Scheme looping idiom [31]: (define cycle-proc (lambda (proc) (let ((loop (call/cc (lambda (k) k)))) (begin (proc) (loop loop)))))

In the present paper, we would like to study the expressive power of call/cc in as simple a setting as possible, without the possibility of using it for recursion, so we will be concerned with the second idiom mentioned in the quote: the argument of call/cc may produce some function that may call the escape function as its value. Arguably the simplest procedure that has this kind of behaviour is the following procedure that calls the escape function (captured continuation): (call/cc (lambda (k) (lambda (x) (k (lambda (y) x)))))

This idiom will be our canonical example of a control e ect that is quite unlike what one would expect from a forward goto. A consequence of the addition of computational e ects to a language is that it becomes more sensitive to details of the evaluation. Consider ((lambda (x) #f) (k #t)). One cannot dispense with evaluating the argument, even if the result is thrown away. In the presence of continuations, the context (call/cc (lambda (k) [ ])) can separate ((lambda (x) #f) (k #t))

and

#f

giving the answers #t and #f, respectively. This simple use of an exit is perhaps the most elementary example of the discriminating power of call/cc. But there are more subtle \non-equivalences" due to call/cc's \de ance of the usual substitution rules" [17] | some of which were in fact conjectured to be equivalences. Furthermore, these non-equivalences can be shown using the control e ects (upward continuations) mentioned above. As far as formalisms for continuations are concerned, considerable progress has been made. At any rate, de nitional interpreters or cps transforms have been around for decades. Yet as we will see the conceptual subtleties remain when one

3

reasons informally (as in: \continuations without state are too weak an e ect to merit interest"), or pre-formally (when trying to nd plausible conjectures, before formally verifying them). Strachey and Wadsworth [32] put somewhat less emphasis than Landin and Reynolds on the possibility of passing arguments to continuations. So in that setting, state in the form of assignable labels is necessary for upward continuations. Furthermore, when upward continuations are used in the literature, they are commonly used in conjunction with state. For instance, when \obtaining coroutines with continuations" [14, 23] the local control state of each coroutine stores its current continuation. This is done to enhance modularity, and should not be taken as an indication that state is somehow necessary for upward continuations. 1.1. Related Work

The examples that we present show that rst-class continuations are good at distinguishing, or discriminating between, -terms, and thus that they are a \powerful" e ect in the sense of Felleisen's \expressive power" [6], or Boudol's \discriminating power" [3]. Landin [17, 18] already used inequivalences to show the additional expressive power of J (though the exact inequivalences used there are idiosyncrasies of J and do not generalise to call/cc [36]). The particular idiom of using a continuation twice that we call arg-fc in the sequel was already used as a counterexample to polymorphic type assignment [15]. Grin [10] used a continuation twice for establishing the excluded middle in his formulae-as-types interpretation of control operators. The expressive power of call/cc is discussed (and branded unreasonable) by Riecke [25, 20]; it was further analysed in Felleisen's expressiveness framework [6]. In both cases, downward continuations are considered. Here, by contrast, we focus on upward continuations and control e ects that are quite di erent from those of downward continuations. In Haynes's paper on \logic continuations" [11], upward failure continuations are used to implement nondeterminism (already mentioned by Reynolds [24]) by way of backtracking. We found this connection to backtracking illuminating even for the highly idealised (stateless) setting of the present paper. 1.2. Outline

We recall the standard idealised language and the semantic setting that we will use, a call-by-value -calculus with call/cc, in Section 2. In Section 3, two functions that use their current continuation twice are discussed. Such an idiom forms the basis of the counterexamples in the rest of the paper. Section 4 refutes Filinski's [8] view of the total morphisms as e ect-free, and as a corollary the \idempotency hypothesis for control e ects", which was informally conjectured by Amr Sabry. In Section 5, we give further evidence for the expressive power of call/cc by showing that two -terms that cannot be distinguished in various extensions of \lazy" or weak call-by-name -calculus can be separated with an analogue of the technique we had used in call-by-value. Section 6 concludes and points towards further work.

4

A reader chie y interested in call/cc as part of Scheme or Standard ML of New Jersey may wish to skim much of the formalism, focusing on Section 3 and the Scheme code in the gures. Conversely, a more theoretically-minded reader could skim Section 3 and trace de nitions and proofs from Section 2 onwards. 1.3. Acknowledgements

We should like to point out that the conjectures that we will study were interesting attempts at pinning down continuations in terms of global algebraic properties; the fact that they can be refuted does not distract from that: on the contrary, it should be seen as proof that continuations are suciently interesting and subtle to surprise even the experts. In that regard, I would like to thank Andrzej Filinski for telling me about the idempotency hypothesis. Thanks also to Peter Landin, Gerard Boudol, Davide Sangiorgi, Paul Levy and Jim Laird for discussions. Several people made valuable comments on the draft, in particular Peter O'Hearn. The anonymous referees spotted bugs and made many suggestions for improvement.

2. Setting We will be concerned with the discriminating power that call/cc adds to the callby-value -calculus, more speci cally with the notion of separable terms adapted from the theory of the -calculus [2]. Roughly speaking, two terms are separable if, in some context, they give di erent answers, say true and false. If two terms are separable, they are not equivalent in any reasonable sense. The counterexamples rely on what amounts to a very small subset of Scheme consisting of lambda and call/cc and some base type constants, like the booleans #t and #f. Some syntactic sugar, such as let and begin is used for readability, but could be expanded out. When addressing what we call copyability, we also need cons, car and cdr. Where the top-level define is used, it could be eliminated in favour of lambda. With the exception of Section 5, which deals with an untyped -calculus, we stay inside a simply-typed subset. The classical way of giving the semantics of a language with control operators is via a cps interpreter [24], or a cps transform. We recall cps transforms, but also give an operational semantics based on evaluation context in the style developed by Felleisen and others, as this allows calculations on source-level terms that may be helpful for seeing what is going on. 2.1. The language

Up to minor variations, both the language and its semantics are taken o -the-shelf from the continuations literature: see the typing of callcc in Standard ML [5, 13] for the language and its type system, and work by Felleisen and others [6, 30] for the evaluation-context operational semantics.

5

We recall the type system for continuations in ML [5]: ?; x :  ` x : 

?; x :  ` M :  ? ` x:M :  ! 

?`M :! ?`N : ? ` MN :  ? ` M : : !  ? ` M : : ? ` N :  ? ` callcc M :  ? ` throw M N :  ? ` M :  1  2 ? ` M i : i ? ` (M1 ; M2 ) : 1  2 ? ` i M : i We choose as our idealised language a fragment of ml rather than Scheme, because it is trivial to move from the typed continuations of an ml fragment to Scheme, while the other direction may be less obvious. One consequence of the simple typing is that all programs terminate (in fact, this would hold even if the language were polymorphic [15]); the phenomena that we study manifest themselves even in such a simple setting (Section 5, however, is inherently about untyped -calculus.) Some syntactic sugar is added as follows: let

x = N in M def = (x:M ) N M ; N def = (x:N ) M x 2= FV(N )

2.2. The cps transforms De nition 1.

The call-by-value cps transform ( ) [24, 22, 5] is de ned as follows:

x x:M MN callcc M throw M N (M; N ) j M

= = = = = = =

k:kx k:k(xk:Mk) (k:M (m:N (n:mnk)) k:M (f:fkk) k:M (m:N (n:mn)) k:M (m:N (n:k(p:pmn))) k:M (m:m(x1 x2 :kxj ))

The transform for pairs makes use of a \classical" encoding similar to Grin's [10]. 2.3. Felleisen-style operational semantics

We give an evaluation-context semantics in the style developed by Felleisen [30, 6].

6

De nition 2. Values V , evaluation contexts E and general terms M are given by the following BNFs: V ::= x j x:M

j x:M j (V; V ) E ::= [ ] j EM j V E j throw E M j throw V E j (E; M ) j (V; E ) j i E M ::= V j MM j j throw M M j callcc M j (M; M ) j i M

A redex is of the following form: R ::= (x:M )V j callcc M j throw ( x:M ) V j i (V1 ; V2 ) A closed term M is either a value, or it can be decomposed as M = E [R] into a redex R and an evaluation context E . Such a decomposition is unique. (Proof by induction on M .) The operational semantics is given by decomposing the term into an evaluation context and a redex. E [(x:M )V ] ! E [M [x 7! V ]] E [callcc M ] ! E [M ( x:E [x])] E [throw( x:M )V ] ! M [x 7! V ] E [i (V1 ; V2 )] ! E [Vi ]

-abstractions (like exception packets in ml) are not part of the programming language, but there are terms that reduce to them. The result of a term (program) M is the value V such that M ! V . This is essentially the same semantics as devised by Felleisen, except that it avoids the abort operator A. Felleisen's rules are closer to Scheme in that the continuation is wrapped into an escape procedure by call/cc: E [A M ] ! M E [call=cc M ] ! E [M (x:AE [x])] 2.4. Separating terms

Adapting a notion from the -calculus [2] to the call-by-value setting, given values T and F which we regard as distinct, we say that two terms M and N are separable iff there is a context C such that C [M ] ! T and C [N ] ! F

7

In an idealised calculus, one could always choose the distinct values as the -calculus booleans T def = xy:x and F def = xy:y In Scheme we cannot directly observe the di erence between procedures, so it is more convenient to pick ground type constants, say the booleans #t and #f. Note that this notion of separability is stronger than two terms merely not being observationally equivalent: we can observe them being di erent, rather than observing termination in one case and nothing in the other. A consequence of this observability of the di erence is that we can machine-verify it by an evaluator for the operational semantics, i.e., Scheme or ml. By contrast, if we were to distinguish terms by showing that one yields an answer and the other diverges, a separate proof of its divergence would be required, as no nite amount of observation could demonstrate this. In the sequel, we will regard -equality of the cps transforms as a strong notion of equivalence of programs, and separability as a strong notion of inequivalence. (The usual notion of operational equivalence is properly contained between -equivalence of the cps transforms of two terms, and their inseparability. See also Sitaram and Felleisen on full abstraction [30].) To make sure that these two notions are not in con ict, we need some fairly standard lemmas relating the operational semantics and the cps transform. (See also Sitaram and Felleisen's adequacy results [30].) The call-by-value cps transform is extended as follows:

z:M = k:k(z:M (x:x)) [ ] = k:k EM = k:E (f:M (x:fxk)) V E = k:V (f:E (x:fxk)) throw E M = k:E (h:M (x:hx)) throw V E = k:V (h:E (x:hx)) (E; M ) = k:E (x:M (y:k(p:pxy))) (V; E ) = k:V (x:E (y:k(p:pxy))) j E = k:E (m:m(x1 x2 :kxj ))

De nition 3.

Lemma 1 V (x:M ) = M [x 7! V ] Lemma 2 E [M ] = M  E

Proof: Induction on E . Lemma 3 If M ! M 0 , then M (x:x) = M 0(x:x). Proof: Using Lemmas 1 and 2, one veri es that: E [(x:M )V ] = E [M [x 7! V ]]

8

E [i (V1 ; V2 )] = E [Vi ] and

E [callcc M ](x:x) = E [M ( x:E [x])](x:x) E [throw( x:M )V ](x:x) = M [x 7! V ](x:x)

Proposition 1 If M = N , then M and N are not separable.

Proof: Suppose M ! T and N ! F. By Lemma 3, F(x:x) = T(x:x). Hence for any two -terms P and Q, P = Q. Contradiction.

3.

arg-fc

and twice/cc

We have already seen a term that uses its current continuation twice; in our idealised notation, it is a one-liner:

k:x:throw k (y:x))

callcc(

To emphasise the fact that k is invoked twice, we could add another (technically redundant) throw immediately after k is captured:

k:throw k (x:throw k (y:x)))

callcc(

This term passes a function to its current continuation k. When this function is called with an argument x, the constant function always returning that argument is passed to k. Hence the function eventually (on the second call to the current continuation) returned by the term is the function always returning the argument to the rst call. For example (assuming for the moment that map proceeds over the list left-toright, rather than in unspeci ed order [16]), we have (map (call/cc (lambda (k) (lambda (x) (k (lambda (y) x))))) (list 1 2 3 4))

(1 1 1 1) The answers printed by the interpreter are written in slanted font. (Some readers may wonder how this can work without assignment ; in Section 4 we will consider in detail the step-by-step evaluation of a very similar example. However, in the remainder of this Section, we will pursue the analogy with assignment a little further.) We can regard this as a solution to the following continuation programming exercise:

9

(define arg-fc (lambda () (call/cc (lambda (k) (k (lambda (x) (k (lambda (y) x))))))))

Figure 1.

arg-fc

(in Scheme)

(define arg-fc-save-arg (lambda () (let ((fc #t) (arg 'anything)) (lambda (x) (if fc (begin (set! fc #f) (set! arg x))) arg))))

Figure 2. An analogue of arg-fc with local state for its argument

(define arg-fc-proc (lambda () (letrec ((f (lambda (x) (begin (set! f (lambda (y) x)) x)))) (lambda (z) (f z)))))

Figure 3. An analogue of arg-fc with local state for the procedure

10

De ne a function f such that all calls to f return the argument of the rst call of f. Do not use state. We name this arg-fc, for \argument of rst call". At rst sight, it seems hard to see how to write such a function without state: the obvious solution, after all, uses two variables (or in ml, references): a nonlocal variable to hold the argument of the rst invocation and a boolean ag to record if the function has been called before (if not, then the variable needs to be assigned). See Figure 2 for a version of arg-fc with local state. We can give a better analogue of arg-fc with continuations by using a function that updates its own de nition when it is called (Figure 3). Note that the last occurrence of f needs to be wrapped or protected by a lambda for this to work (see \Scheme and the Art of Programming" [29] for a discussion on this and safe-letrec). The connection to state in Figure 3 gives evidence for a point already made by Landin about what would now be called upward continuations: This situation is similar to that arising when label assignment or procedure assignments are introduced [: : :] ([17, pp10f], emphasis mine). The above is meant as an illustration: we hope that code is more concise than a prose narrative of the behaviour of arg-fc, or at least a helpful addition to it. We do not, however, claim that the versions of arg-fc with state are equivalent to the one with control; for instance a context which itself uses state can easily separate them: (define backtrack? (lambda (testee) (let ((ratchet (list 'anything #f #t))) (let ((f (testee))) (begin (set! ratchet (cdr ratchet)) (f 'anything) (car ratchet)))))) (backtrack? arg-fc)

#t

(backtrack? arg-fc-proc)

#f

A better metaphor than state may be backtracking by way of upward failure continuations [11]. The function

x:throw k (y:x) which is passed to the current continuation k could be seen as an upward failure continuation in the sense that once it is applied to an argument x, it fails, by causing backtracking, so that the constant function y:x is returned to k instead.

11

Although the very point of backtracking seems to be to combine it with (local) state, so that a di erent value can be passed to the continuation upon each backtrack, this restricted form of backtracking is nonetheless possible even without state. In the above, we were concerned with source-level terms. We can also arrive at terms using their current continuation twice by working at the cps level instead. Consider the cps transform of the function twice def = f:x:f (fx).

f = x:f (fx) = k:k(xh:(q:fxq)(y:fyh)) twice

What is relevant here is the idiom (q:fxq) (y:fyh) for the function composition f  f in cps. (We could of course simplify this to fx(y:fyh), but for the present purpose it is better to leave the intermediate continuation q named.) The rst call to f is passed an argument x together with a continuation q pointing to the place where the second call to f is awaiting its argument y; this second call is thus fed the result of the rst call together with the overall result continuation h. We can write, modulo uncurrying, the same cps term when f is not some function, but the current continuation (q:k(x; q)) (y:k(y; h)) The only slight diculty in translating this cps term into Scheme or ml lies in implementing the binding of the continuation q. We do this by -abstracting on q and applying call/cc. In Standard ML of New Jersey this is quite straightforward: fun twicecc (x,h) = callcc(fn k => (fn y => throw k (y,h)) (callcc(fn q => throw k (x,q)))); twicecc : 'a * 'a cont -> 'a * 'a cont

A minor diculty speci c to Scheme, however, is that prior to the addition of call-with-values in the latest (revised5 ) report [16], one cannot have multiargument continuations as in (k x q) by analogy with throw k (x,q) above. In the minimal Scheme that we use in this paper, one would have to put the arguments into a list, which is then the single argument to the continuation, i.e., (k (list x q)). (See Figure 4 on page 12). As explained in the present author's PhD thesis [34], this is an instance of a more general translation from cps back to the source language, generalising what Sabry calls \Continuation Grabbing Style" [26]. We could also give a more rigorous derivation of twice/cc from twice [34, 35, 33]. Written as the cps term kx(y:kyh), twice/cc seems to be the simplest way of using the current continuation twice. Furthermore, it does so without mixing

12 (define twice/cc (lambda (p) (call/cc (lambda (k) ((lambda (y) (k (cons y (cdr p)))) (call/cc (lambda (q) (k (cons (car p) q)))))))))

Figure 4.

twice/cc

in continuation-grabbing style

continuations and higher-order procedures. Despite being much simpler to write at the source level, arg-fc is more complicated than twice/cc as a cps term: k:k(xp:k(yq:qx)) In previous versions of the material presented here [34, 35, 33], we put greater emphasis on twice/cc. We use arg-fc here instead, as its de nition and the separating contexts are easier to understand, particularly in direct style, rather than cps. Nonetheless, it is insightful to have another, and quite di erent, function that gives rise to similar phenomena by also using a continuation twice.

4.

arg-fc

is discardable, but not copyable

Filinski [8] developed a framework for continuation semantics on the assumption that discardable (there called total) terms can be considered e ect-free. Here, a term is called discardable iff evaluating it and then throwing away the result is the same as not evaluating it at all. Similarly, a term is called copyable if copying the term and copying its value are the same. De nition 4. Given a notion of equality on terms, we say a term M is copyable iff (x:(x; x))M = (M; M ). M is called discardable iff (x:y)M = y.

In the sequel, when we prove two terms to be equal, we use the -equality of their

cps transforms, which is a very strong and robust notion of equality (used, for instance, by cps compilers for optimization). This implies that the terms are also

equal in weaker senses, such as not being separable or typical notions of contextual equivalence. Dually, when we show two terms not to be equal, we show they can be separated, in which case they cannot be considered equal in any reasonable sense. Exits are evidently not discardable, where by an exit we mean an invocation of a continuation with an argument that does not itself contain the continuation, syntactically throw k V with k not free in V . As an example, consider (x:F )(throw k T ) and F

13

These can be separated by the context callcc(k:[ ]): callcc(k:(x:F ) (throw k T )) ! T callcc(k:F ) ! F Discarding the value of the computation by applying a term x:M with x not free in M to it does not amount to discarding the exit. One could therefore hope that the inability to be discarded this way is somehow characteristic of control e ects. This characterisation was attempted by Filinski [8], in that it was assumed that the subcategory of total maps had nite products; and this amounts to saying that all discardable terms should also be copyable. Remark. A term of the form throw M N is copyable.

Proof:

(throw M N; throw M N ) k:(k:M (m:N (n:mn)))(x:(k:M (m:N (n:mn)))(y:k(p:pxy))) k:M (m:N (n:mn)) k:(k:M (m:N (n:mn)))(x:k(p:pxx)) k:(k:k(xk:(x; x)k))(f:throw M N (y:fyk))  (x:(x; x)) (throw M N )

= = = =

Remark. A term of the form callcc(e:M ), where e is used only as an exit from

some evaluation context E , i.e., M = E [throw e V ] with e not free in V , is also copyable. In fact, one can simply erase the E and throw. Proof: Let V be a value with e not free in V . Hence V = k:kN for some N with e and k not free in N . Using E [M ] = M  E (Lemma 2), we can simplify: callcc(e:E [throw e V ])  k:(k:k(ek:E [throw e V ]k))(f:fkk) = k:(k:k(ek:[throw e V ](Ek)))(f:fkk) = k:(k:k(ek:(k:eN )(Ek)))(f:fkk) = k:(k:k(ek:eN )))(f:fkk) = k:(f:fkk)(ek:eN ) = k:(ek:eN )kk = k:kN

 V

Copyability then follows from the soundness of the -value law. So we now turn to a use of callcc that goes beyond the simple exit idiom in Remark 4, speci cally arg-fc. Let

14

A def = callcc(k:x:throw k (y:x)) Lemma 4 A is discardable.

Proof: Straightforward:

(x:y) (callcc(k:x:throw k (y:x)) = y Let T and F be distinct values. As a a rst step towards showing the failure of copyability, we explain how (A T ; A F ) and (f:(f T ; f F )) A give di erent answers. The point is that the following derivations show the essence of the use of backtracking to separate terms. The same phenomenon is at work with copyability, but the larger separating context makes the corresponding derivation somewhat unwieldy to display. Remember that the semicolon is syntactic sugar, i.e., M ; N def = (x:N ) M for x 2= FV(N ) Lemma 5 (A T ; A F ) and (f:(f T ; f F )) A can be separated.

Proof:

Let E = ([ ] T ; callcc(k:x:throw k (y:x)) F ). The rst derivation is (callcc(k:x:throw k (y:x)) T ; callcc(k:x:throw k (y:x)) F ) E [callcc(k:x:throw k (y:x))] E [(k:x:throw k (y:x)) ( z:E [z ])] E [x:throw ( z:E [z ]) (y:x)] ((x:throw ( z:E [z ]) (y:x)) T ; callcc(k:x:throw k (y:x)) F ) throw ( z:E [z ]) (y:T ); callcc(k:x:throw k (y:x)) F E [y:T ] ((y:T ) T ; callcc(k:x:throw k (y:x)) F ) (T ; callcc(k:x:throw k (y:x)) F ) callcc(k:x:throw k (y:x)) F (k:x:throw k (y:x)) ( z:zF ) F (x:throw ( z:zF ) (y:x)) F throw ( z:zF ) (y:F ) (y:F ) F

 ! !  ! !  ! ! ! ! ! ! !F

15

But in the other case, the backtracking a ects the result. Formally, the backtracking is manifested in the re-use of the evaluation context E = (f:(f T ; f F )) [ ]. let f = callcc(k:x:throw k (y:x)) in (f T ; f F )  (f:(f T ; f F )) (callcc(k:x:throw k (y:x))) ! E [(k:x:throw k (y:x)) ( z:E [z ])] ! (f:(f T ; f F )) (x:throw ( z:E [z ]) (y:x)) ! ((x:throw ( z:E [z ]) (y:x)) T ; (x:throw ( z:E [z ]) (y:x)) F ) ! (throw ( z:E [z ]) (y:T ); (x:throw ( z:E [z ]) (y:x)) F ) ! E [y:T ] = (f:(f T ; f F )) (y:T ) ! (y:T ) T ; (y:T ) F ! T ; (y:T ) F ! (y:T ) F

!T

This derivation shows quite clearly the characteristic control behaviour of arg-fc.

f is applied to T ; the result of this call is then completely forgotten, as \;" causes evaluation to happen, but discards the result. But the subsequent call of f with F has been a ected, in that it will return T , the argument that was passed to the rst call. We could write the above in Scheme as follows: (let ((f (arg-fc))) (begin (f #t) (f #f)))

#t (begin ((arg-fc) #t) ((arg-fc) #f))

#f We have argued that A can be understood as generating a function that backtracks as soon as it is applied to an argument. What is essential, therefore, is whether we have functions generated from the same copy of A or from separate copies. In the rst case the calls will in uence each other; in the second case, they will not. With that understanding, it should be possible to reduce the failure of copyability to Lemma 5. It is in fact possible by appealing to the cps semantics, corroborating the intuition we have developed about A.

16

(define copy-val (lambda (M) (let ((x (M))) (list x x)))) (define copy-comp (lambda (M) (let ((x (M))) (let ((y (M))) (list x y))))) (define copy-separator (lambda (copier) (let ((funs (copier arg-fc))) (let ((f (car funs))) (let ((g (cadr funs))) (begin (f #t) (g #f)))))))

Figure 5. Copying a computation, copying its value, and a separating context

17

Proposition 2 Discardability does not imply copyability.

Proof: A is discardable by Lemma 4. To refute copyability, let M = A and let

the separating context be given by E = (p:1 pT ; 2pF ) [ ] For a value V , let V 0 be the -term such that V  k:kV 0 . Note that we can simplify j pV = k:p(x1 x2 :xj V 0 k). Then we have, by expanding out the backtracking of the rst occurrence of A: (p:1 pT ; 2 pF ) (A; A) = k:(h:A(m:A(n:h(q:qmn))))(p:p(x1 x2 :x1 T 0(z:p(x1 x2 :x2 F 0 k)))) = k:A(m:A(n:mT 0 (z:nF 0 k))) = k:(m:A(n:mT 0(z:nF 0 k)))(yh:hT 0 ) = k:(m:mT 0 (z:A(n:nF 0 k)))(yh:hT 0 ) = k:A(m:mT 0 (z:A(n:nF 0 k))) = AT ; AF For the other case, we can appeal to standard equivalences validated by the cps transform: (p:1 pT ; 2 pF )((x:(x; x))A) = (x:(p:1 pT ; 2pF )(x; x))A = (x:1 (x; x)T ; 2 (x; x)F )A = (x:xT ; xF )A By Proposition 1, the above and Lemma 5 imply that: (p:1 pT ; 2 pF )((x:(x; x)A)) ! T (p:1 pT ; 2pF )(A; A) ! F So (x:(x; x))A and (A; A) can be separated, even though A is discardable. The Scheme code for separating the two terms is in Figure 5. (Explicit sequencing by way of let is necessary to make it legal Scheme [16].) Evaluation produces: (copy-separator copy-val)

#t

(copy-separator copy-comp)

#f

4.1. Discardable and copyable are orthogonal

Considering that values are copyable and discardable, whereas jumps (throw) are copyable but not discardable, we can summarise that copyable and discardable are orthogonal.

18

Table 1. Copyability and discardability are orthogonal copyable not copyable discardable x x:M (twice/cc a) (arg-fc) not discardable throw M N (call/cc h)

While it is evident that values can be discarded and jumps cannot, the upperright box in the table (discardable, but not copyable) was previously thought to be unoccupied, in that Filinski [8] thought that discardability, separating the top from the bottom row, was sucient for separating value from all control e ects. None of the formal proofs in Filinski's categorical account of continuations are shown to be actually false. What has been shown to be erroneous is the underlying assumption that all discardable morphisms are copyable. That is, the developments from the (categorical) axiomatisations may still be sound, but these axioms fail to characterise e ect-free computations in the presence of rst-class continuations. A corollary of this table is that a rst-order jump like throw k V with k not free in V , is not maximally e ectful. When it comes to being e ectful, it is self-defeating in that it forgets the current continuation. This implies that is it copyable, because one jump (x:(x; x))(throw k 42) is as good as two (throw k 42; throw k 42) The rst jump will ignore it continuation containing the second jump, so that it does not matter whether the latter is present or not. By contrast, the quintessential rst-class jump (call/cc h) (where h is a continuation), is not oblivious to its continuation, as this is passed as an argument. This makes call/cc suciently sensitive to its continuation to defy copying. We have argued elsewhere [35, 34] that a better criterion for the e ect-freeness of a program in the presence of continuations is centrality, a kind of insensitivity to evaluation order. This corresponds roughly to another traditional use of continuations: the use of breakpoints from which a program may be restarted [29]. 4.2. Control is not an idempotent e ect

A similar context to the one used for copyability also gives us a counterexample to Amr Sabry's informal conjecture that, in Filinski's words, \control is an idempotent e ect". Thanks to Andrzej Filinski for pointing out to me that the refutation of the idempotency hypothesis follows as a corollary from Proposition 2 [Andrzej Filinski, personal communication]. The idempotency conjecture holds that (x:(x; x))M should be indistinguishable from (x:x(y:(x; y)))MM . We say, somewhat loosely, that a particular language construct is \idempotent" if this equivalence holds even if M uses that construct; for instance, \jumps are an idempotent e ect" is shorthand for saying that the equivalence holds for terms of the form M  throw P Q. To explain the \idempotent e ect" slogan, consider assignment. An example of an \idempotent e ect" is an assignment like (set! x 42). This expression has an e ect, because the value in the location x is changed, but the e ect is idempotent,

19 (define idempotency-separator (lambda (testee) (let ((procs (testee))) (begin ((car procs) #t) ((cdr procs) #f))))) (define arg-fc-1copy (lambda () (let ((x (arg-fc))) (cons x x)))) (define arg-fc-2copies (lambda () (let ((x (arg-fc))) (let ((y (arg-fc))) (cons x y)))))

Figure 6. Refutation of the idempotency hypothesis

in the sense that assigning the same value to x twice is as good as doing it once. By contrast, the assignment (set! x (+ x 1)) is not idempotent, as evaluating it twice increments x by 2. It may be interesting to compare the \idempotency" of assignment to control transfers: after all, a jump could be seen as an assignment to the program counter. In fact, jumps are idempotent. More precisely: Remark. Let M = throw P Q. Then (x:(y:(x; y)))MM = (x:(x; x))M Because (x:(y:(x; y)))MM = (M; M ), this follows from Remark 4. Continuing the analogy with assignment, we could compare arg-fc to (set! x (+ x 1))

in that it both reads and writes to the program counter, so to speak. The analogy becomes clearer if we write it as callcc(k:throw k (x:throw k (y:x))) Here the rst throw may be seen as an assignment to the program counter. But k is also saved in the closure (x:throw k (y:x)), as it were. So we would expect arg-fc, like (set! x (+ x 1)) not to be idempotent | which is indeed the case. More formally, we have as a consequence of the failure of copyability:

20

Corollary 1 There is a term M such that

(x:(x; x))M

and

(x:(y:(x; y)))MM

can be separated. Proof: Because (x:(y:(x; y)))MM = (M; M ), the result follows from Proposition 2.

With idempotency-separator de ned in gure 6, we have: (idempotency-separator arg-fc-1copy)

#t

(idempotency-separator arg-fc-2copies)

#f

The conjecture could be perhaps be supported by informal arguments about rstorder jumps. These can be copied essentially because they are oblivious to their continuation, so that it does not matter if another jump follows. Hence the idempotency could be defended for values, as well as for rst-order jumps. What it fails to take into account are terms that do not simply pass something to the current continuation, but do not ignore it either. There seems to be an assumption of a kind of excluded middle here, along the line of: functions in continuations semantics can return a value or else they are goto's. To the extent that that such ideological conclusions can be drawn from this example, we should like to argue that it is misleading to think of rst-class control as a form of non-termination due to jumping (we therefore prefer to use \discardable" rather than \total" [8]).

5. Separating by extending (weak) call-by-name In this section, we proceed to generalise our separation technique from call-byvalue to separating (weak) call-by-name terms. Speci cally, we show that upward continuations are powerful enough to separate the terms x:xx and x:x(y:xy) when the appropriate operators are added to a weak (sometimes called \lazy") call-by-name -calculus. The terms x:xx and x:(y:xy) are one of the canonical examples cited as evidence of the expressive power of the -calculus [28]. Here, the key ingredient for making the distinction is a term that uses its current continuation twice. We use \call-by-name" in the sense in which it is used in the continuations literature [22]; though one often nds the term \lazy" following Abramsky [1] | unfortunately \lazy" itself is often used to mean memoizing. To avoid confusion between this notion and the \real" -calculus (e.g.,[2]), we qualify call-by-name with \weak" to indicate that no reduction takes place under a  [3]. In related work, various computational e ects have been added to the weak callby-name -calculus to increase its expressive power: nondeterminism [27], \resources" [3] and \parallel observers" [4], a mixture of concurrency and call-by-value.

21

5.1. Extending the weak call-by-name -calculus

An evaluation context semantics for the weak call-by-name -calculus could be de ned as follows: E ::= [ ] j EM and E [(x:M )N ] !n E [M [x 7! N ]] However, if all evaluation contexts were of the form [ ]M1 : : : Mj , a control operator that gives access to such contexts would not allow us to get the backtracking behaviour that was so crucial for arg-fc in call-by-value and whose discriminating power we want to study. So we nd it necessary to extend the calculus with a construct for call-by-value contexts, which can then be seized by callcc. Nonetheless, -abstractions and applications are call-by-name (albeit \weak"). De nition 5. Values V , evaluation contexts E and general terms M are given by the following BNFs: V ::= x:M j x:M M ::= V j x j MM j

M j throw M M j x = M in M E ::= [ ] j EM j throw E M j letval x = E in M callcc

letval

Reduction !n is de ned as follows: E [(x:M )N ] !n E [M [x 7! N ]] E [callcc M ] !n E [M ( z:E [z ])] E [throw ( z:M ) N ] !n M [z 7! N ] E [letval x = V in M ] !n E [M [x 7! V ]]

Remark. The calculus in De nition 5 could be given a cps semantics by extending the call-by-name cps transform [22] as follows. x = k:xk x:M = k:k(xk:Mk) MN = k:M (m:mNk) callcc M = k:M (f:f (p:pk )k ) throw M N = k:M (m:N (n:mn)) letval x = N in M = k:N (y:(x:Mk )(p:py ))

22

The letval construct ts naturally into this semantics | not surprisingly so, since weak call-by-name factors over call-by-value [12]. 5.2. Separating x:xx and x:x(y:xy)

In a weak -calculus,  (x:xx)(x:xx) and y: are not operationally equivalent. So the problem arises of distinguishing terms of the form M and y:My (y fresh), in contrast with the ordinary (non-weak) -calculus, where separation is considered only up to  [2]. At rst sight, it would seem that a very drastic control e ect would be well suited for the purpose of discriminating x and y:xy. But again, the disruptive nature of exits turns out to be self-defeating as far as discriminating is concerned. For suppose we plan to discriminate x and y:xy by plugging an exit into x as above. When we then try to move on to discriminate the slightly more complicated terms xx and x(y:xy), our separating context is hoist with its own petard: in both cases, the exit occurs during the rst call to x, leaving no possibility to discriminate its argument, x and y:xy, respectively. We have encountered this self-defeating nature of exits before. Again it turns out that not all control e ects are like exits. The lynch-pin of the development is once again arg-fc. The term we use is the same as in call-by-name, even though both callcc and  have now a quite di erent semantics. Let A = callcc(k:x:throw k (y:x)). As in call-by-value, much depends on the fact that A is not overly disruptive; more precisely, in some contexts, it behaves like the identity in that the backtracking does not a ect the result (compare Lemma 5). Lemma 6 For all evaluation contexts E and terms M , E [AM ] !n E [M ].

Proof:

!n !n !n !n !n

E [(callcc(k:x:throw k (y:x)))M ] E [(k:x:throw k (y:x))( z:E [zM ])M ] E [(x:throw ( z:E [zM ]) (y:x))M ] E [throw ( z:E [zM ]) (y:M )] E [(y:M )M ] E [M ]

Lemma 6 also corroborates that we need to have evaluation contexts other than those of the form [ ]M1 : : : Mj if we want to use A for separating. Proposition 3 In the weak call-by-name -calculus extended with

callcc and as in De nition 5, the terms x:xx and x:x(y:xy) can be separated. Proof: We show that for each value P and term Q, there exists an evaluation context E such that letval

23

E [x:xx] !n P Let

E = E2 =

and

E [x:x(y:xy)] !n Q

f = [ ]A in letval z = fP in fQ letval f = [ ] in letval z = fP in fQ First, consider x:xx. Using Lemma 6, we have E [x:xx]  E2 [(x:xx)A] !n E2 [AA] !n E2 [A]  E2 [callcc(k:x:throw k (y:x))] !n E2 [(k:x:throw k (y:x))( z:E2 [z ])] !n E2 [x:throw ( z:E2 [z ]) (y:x)] !n letval z = (x:throw ( z:E2 [z ]) (y:x))P in (x:throw ( z:E2 [z ]) (y:x))Q !n letval z = throw ( z:E2 [z ]) (y:P ) in (x:throw ( z:E2 [z ]) (y:x))Q !n E2 [y:P ]  letval f = y:P in letval z = fP in fQ !n letval z = (y:P )P in (y:P )Q !n letval z = P in (y:P )Q !n (y:P )Q !n P By contrast, for x:x(y:xy), we have: E [x:x(y:xy)]  E2 [(x:x(y:xy))A] !n E2 [A(y:Ay)] !n E2 [y:Ay] !n letval z = (y:Ay)P in (y:Ay)Q !n letval z = AP in (y:Ay)Q !n letval z = P in (y:Ay)Q !n (y:Ay)Q !n AQ !n Q letval

It may be worthwhile to compare the proof of Proposition 3 with that of Lemma 5. In each case, we use backtracking to communicate arguments between di erent

24

calls of the same function, so as to separate a term from another in which the backtracking is too localised to a ect the outcome. This narrative can be made precise in the re-use of evaluation contexts or, in cps, continuations. Sangiorgi [27, Theorem 8.5] proves that the weak call-by-name -calculus (there called \lazy"), even if enriched with con uent operators whose reduction rules conform to the so-called GV format, cannot distinguish between x:xx and x:x(y:xy). We refer the reader to Sangiorgi's account [27] for the details of the GV (Groote/ Vaandrager) format. We only remark here that this format is quite a general form of introducing new operators into operational semantics, specifying the result of an operator application in terms of the results of its subterms. Crucially, one thing the GV format does not permit is for a term to depend on an evaluation context, as in Felleisen-style semantics for callcc. Corollary 2 The weak call-by-name -calculus extended with callcc and letval can separate two terms that cannot be discriminated in any extension of the calculus conforming to the GV format. Furthermore, because the letval construct could be de ned in the GV format, we can be sure that the increase in expressive power is due to callcc.

5.3. A generalisation of arg-fc

In the remainder of this section, we give an n-argument generalisation of arg-fc and sketch its use in a general separation technique called Bohming out. We recall the following remark on Bohming out in a weak call-by-name calculus by Boudol and Laneve [3]: [: : :] what is important is to be able to distinguish the successive appearances of a given variable in the head position. As shown by Bohm, the -calculus provides part of this ability. But it generally fails distinguishing subterms like xM1 : : : Mk and y:xM1 : : : Mk y [: : :] The rst ability is due to the traditional Bohm tupling combinator = x1 : : : xn p:px1 : : : xn Pn def In order to make the second kind of distinction (between values and non-values) one uses additional features, broadly speaking computational e ects, that are sensitive to whether they appear under a  or not. We de ne combinators Unj and Pnj as follows:

Unj def = x1 : : : xn :xj = x1 : : : xj :callcc(k:xj+1 :throw k (yxj+2 : : : xn p:px1 : : : xn )) Pnj def

25

Note that Pnj is a hybrid of Pn = x1 : : : xn p:px1 : : : xn and A. When applied to n arguments, Pnj behaves like the tupling combinator. But because it backtracks when it receives the j + 1st argument, it is sensitive to whether it is applied to j arguments, as in Pnj M1 : : : Mj , or more than j , as in y:Pnj M1 : : : Mj y. This generalises our use of A above. This following Lemma makes precise to what extent Pnj behaves like a tupling combinator when applied to n > j arguments. Lemma 7 E [Pnj L1 : : : Ln Unk ] !n E [Lk ]

Proof: E [Pnj L1 : : : Ln Unk ]  E [(x1 : : : xj :callcc(k:xj+1 :throw k (yxj+2 : : : xn p:px1 : : : xn ))) L1 : : : Ln Unk ] j !n E [callcc(k:xj+1 :throw k (yxj+2 : : : xn p:pL1 : : : Lj xj+1 : : : xn )) Lj+1 : : : Ln Unk ] 2 !n E [(xj+1 :throw ( z:E [zLj+1 : : : Ln Unk ]) (yxj+2 : : : xn p:pL1 : : : Lj xj+1 : : : xn )) Lj+1 : : : Ln Unk ] !n E [throw ( z:E [zLj+1 : : : LnUnk ]) (yxj+2 : : : xn p:pL1 : : : Lj+1 xj+2 : : : xn )Lj+2 : : : Ln Unk ] !n E [(yxj+2 : : : xn p:pL1 : : : Lj+1 xj+2 : : : xn )Lj+1 : : : LnUnk ] !n E [Unk L1 : : : Ln ]  E [(x1 : : : xn :xk )L1 : : : Ln] !nn E [Lk ]

As an example, consider these terms (where we assume that x is not free in L1 ; L2; L3 ; N1 or N2 ): M1 = xN1 (x(xL1 L2 L3))N2 M2 = xN1 (x(y:xL1 L2 L3 y))N2 We need to separate xL1 L2L3 and y:xL1 L2 L3 y, while simultaneously bringing them to the top level (\Bohming out"). The rst occurrence of x should behave like the second projection; the second occurrence of x like the rst projection; while the third occurrence of x should be e ectful, so that xL1 L2 L3 can be separated from y:xL1 L2 L3 y. First, let C1 = (x:[ ])P43 RU42 RRRU41, where R is an arbitrary term. Note that C1 [M1 ] !n P43 L1L2 L3 and C1 [M2 ] !n y:P43 L1 L2 L3y Applying Lemma 7 twice, we have: C1 [M1 ]  (x:xN1 (x(xL1 L2 L3))N2 )P43 RU42 RRRU41

26

!n P43 N1 (P43 (P43 L1 L2 L3))N2 RU42 RRRU41 !n P43 (P43 L1 L2 L3 )RRRU41 !n P43 L1L2 L3

(In fact, this holds in any evaluation context E .) Roughly speaking, by plugging in P43 for x and applying the result to the appropriate projections U42 and U41 with R as padding, we can Bohm out the second argument of the rst occurrence and the rst argument of the second occurrence of x, bringing (a substitution instance of) the subterm xL1 L2 L3 to the top level. C1 succeeds in bringing the two di ering subterms to the top level; it remains to separate them. To do so, let arbitrary terms P and Q be arbitrary terms.

E2 = letval f = [ ] in letval z = fP in fQU44 Combining these contexts, let C = E2 [C1 ]. Then we have

C [M1 ] !n P

and

C [M2 ] !n Q

This is only an illustration, and the reader may consult the literature on Bohming out [2, 3]. However, the above encourages the hope that the separation by backtracking as in arg-fc could be re ned to a systematic technique.

6. Conclusions and directions for further work The examples we have considered, while not \unreasonable" in a formal sense, show certain pitfalls one may become entrapped in when trying to reason about continuations in a preformal way, e.g., when thinking by analogy with exceptions or goto. The strand that runs through all the examples is that they are based on using a continuation twice, so that some of the pitfalls can be avoided if this simple fact is kept in mind. Section 5 provides preliminary evidence that call/cc (provided it can seize callby-value contexts) could be similarly discriminating as the -calculus and the lazy -calculus with resources [3]. The construction of a distinguishing context used there could probably be re ned to yield the necessary Bohm-out technique, with a generalisation of arg-fc as the Bohm-out combinator. The ability to use a continuation twice appears to be a crucial aspect of the discriminating power of continuations. The equivalences broken by such multiple use of a continuation may hence be a good criterion for distinguishing rst-class continuations from other forms of control, such as goto, exceptions and one-shot continuations [7]. Although it may be obvious to readers who understand exceptions, it may be worth emphasising that the techniques for discriminating terms by using a continuation twice do not generalise to exceptions. A naive transliteration of arg-fc with exceptions instead of continuations could be attempted like this: fun argfcexn () = let

27 exception e of int -> int in (raise e (fn x => raise e (fn y => x))) handle e f => f end;

However, the function returned by argfcexn does not have the backtracking behaviour that was so crucial for arg-fc; instead it will raise an uncaught exception when called outside the dynamic extent of the handler (despite the fact that the scope of the local exception name e is static). Contrasting the expressive power of call/cc with that of exceptions is beyond the scope of the present paper. We hope to pursue this in future work.

Notes 1. Part of this work was carried out while the author was an EU HCM postdoc at INRIA SophiaAntipolis, France; the author is currently funded from EPSRC grant GR/L54639.

References 1. S. Abramsky and C.-H. L. Ong. Full abstraction in the lazy lambda calculus. Information and Computation, 105(2):159{267, 1993. 2. Henk Barendregt. The Lambda-Calculus: Its Syntax and Semantics. North-Holland, Amsterdam, 1980. 3. Gerard Boudol and Cosimo Laneve. The discriminating power of multiplicities in the calculus. Information and Computation, 126(1):83{102, April 1996. 4. Mariangiola Dezani-Ciancaglini, Jerzy Tiuryn, and Pawel Urzyczyn. Discrimination by parallel observers. In Proceedings, Twelth Annual IEEE Symposium on Logic in Computer Science, pages 396{407. IEEE Computer Society Press, 1997. 5. Bruce Duba, Robert Harper, and David MacQueen. Typing rst-class continuations in ML. In Proc. ACM Symp. Principles of Programming Languages, pages 163{173, January 1991. 6. Matthias Felleisen. On the expressive power of programming languages. In Science of Computer Programming, volume 17, pages 35{75, 1991. Preliminary version in: Proc. European Symposium on Programming, Lecture Notes in Computer Science, 432. Springer-Verlag (1990), 134{151. 7. Daniel P. Friedman and Christopher T. Haynes. Constraining control. In Proc. 12th ACM Symposium on Principles of Programming Languages, pages 245{254, 1985. 8. Andrzej Filinski. Declarative continuations: an investigation of duality in programming language semantics. In D. H. Pitt et al., editors, Category Theory and Computer Science, number 389 in Lecture Notes in Computer Science, pages 224{249. Springer-Verlag, 1989. 9. Daniel P. Friedman, Mitchell Wand, and Christopher T. Haynes. Essentials of Programming Languages. MIT Press and McGraw-Hill, 1992. 10. Timothy G. Grin. A formulae-as-types notion of control. In Proc. 17th ACM Symposium on Principles of Programming Languages, pages 47{58, San Francisco, CA USA, 1990. 11. Christopher T. Haynes. Logic continuations. Journal of Logic Programming, 4:157{176, 1987. 12. John Hatcli and Olivier Danvy. Thunks and the -calculus. Journal of Functional Programming, 7(2):303{319, 1997. 13. Robert Harper, Bruce Duba, and David MacQueen. Typing rst-class continuations in ML. Journal of Functional Programming, 3(4), October 1993. 14. Christopher T. Haynes, Daniel P. Friedman, and Mitchell Wand. Obtaining coroutines with continuations. Journal of Computer Languages, 11(3/4):143{153, 1986.

28

15. Robert Harper and Mark Lillibridge. Operational interpretations of an extension of F! with control operators. Journal of Functional Programming, 6(3):393{417, May 1996. 16. Richard Kelsey, William Clinger, and Jonathan Rees, editors. Revised5 report on the algorithmic language Scheme. Higher-Order and Symbolic Computation (nee Lisp and Symbolic Computation), 11(3):7{105, 1998. 17. Peter J. Landin. A generalization of jumps and labels. Report, UNIVAC Systems Programming Research, August 1965. 18. Peter J. Landin. A generalization of jumps and labels. Higher-Order and Symbolic Computation, 11(2), 1998. 19. Mark Lillibridge. Exceptions are strictly more powerful than Call/CC. Technical Report CMU-CS-95-178, Carnegie Mellon University, July 1995. 20. Albert R. Meyer and Jon G. Riecke. Continuations may be unreasonable (preliminary report). In Proceedings of the 1988 ACM Conference on Lisp and Functional Programming, pages 63{71. ACM, 1988. 21. Robin Milner and Mads Tofte. Commentary on Standard ML. MIT Press, 1991. 22. Gordon Plotkin. Call-by-name, call-by-value, and the -calculus. Theoretical Computer Science, 1(2):125{159, 1975. 23. John C. Reynolds. GEDANKEN | A simple typeless language based on the principle of completeness and the reference concept. Communications of the ACM, 13:308{319, 1970. 24. John C. Reynolds. De nitional interpreters for higher-order programming languages. In Proceedings of the 25th ACM National Conference, pages 717{740. ACM, August 1972. 25. Jon G. Riecke. Should a function continue? Master's thesis, Massachusetts Institute of Technology, 1989. Available as technical report MIT/LCS/TR-459 (MIT Laboratory for Computer Science). 26. Amr Sabry. Note on axiomatizing the semantics of control operators. Technical Report CIS-TR-96-03, University of Oregon, 1996. 27. Davide Sangiorgi. The lazy lambda calculus in a concurrency scenario. Information and Computation, 111(1):120{153, May 1994. 28. Davide Sangiorgi. Lazy functions and mobile processes. Technical Report RR-2515, INRIASophia Antipolis, 1995. 29. George Springer and Daniel P. Friedman. Scheme and the Art of Programming. MIT Press, 1989. 30. Dorai Sitaram and Matthias Felleisen. Reasoning with continuations II: full abstraction for models of control. In M. Wand, editor, Lisp and Functional Programming. ACM, 1990. 31. Guy L. Steele Jr and Gerald J. Sussman. Scheme: An interpreter for extended lambda calculus. AI Memo 349, Massachusetts Institute of Technology Arti cial Intelligence Laboratory, Cambridge, Massachusetts, 1975. 32. Christopher Strachey and C. P. Wadsworth. Continuations: A mathematical semantics for handling full jumps. Technical Monograph PRG-11, Oxford University Computing Laboratory, January 1974. 33. Hayo Thielecke. Continuation passing style and self-adjointness. In Proceedings 2nd ACM SIGPLAN Workshop on Continuations, number NS-96-13 in BRICS Notes Series, December 1996. 34. Hayo Thielecke. Categorical Structure of Continuation Passing Style. PhD thesis, University of Edinburgh, 1997. Also available as technical report ECS-LFCS-97-376. 35. Hayo Thielecke. Continuation semantics and self-adjointness. In Proceedings MFPS XIII, volume 6 of Electronic Notes in Theoretical Computer Science. Elsevier, 1997. URL: http://www.elsevier.nl/locate/entcs/volume6.html. 36. Hayo Thielecke. An introduction to Landin's \A generalization of jumps and labels". HigherOrder and Symbolic Computation, 11(2), 1998.

Suggest Documents