Keep Working, Worker Bee!

8.19.2005

So, I've been working a lot recently revising the R5RS semantics paper for the 2005 Scheme Workshop. In doing so I've discovered a lot of interesting R5RS quirks; here's one I discovered just recently. To my knowledge this hasn't been pointed out before, and it might well be a widespread example of R5RS nonconformance among Scheme implementations.

The R5RS seems to avoid specifying how continuations interact with the top-level environment deliberately, the only discussion being: "The continuation represents an entire (default) future for the computation. If the expression is evaluated at top level, for example, then the continuation might take the result, print it on the screen, prompt for the next input, evaluate it, and so on forever" (section 6.4). However, R5RS section 5.1 says: "At the top level of a program (begin ...) is equivalent to the sequence of expressions, definitions, and syntax definitions that form the body of the begin." So because when you type in (begin E1 E2 ...)that's exactly the same thing as just typing in E1 followed by E2 and so on, whatever continuations do with the top level, they should do the same thing whether or not they're in a top-level begin form, right?

I tested every Scheme implementation I could get my hands on, and this wasn't true for any of them when it comes to continuations. Here are the expressions I used:
(define num 0)
(define k (call-with-current-continuation (lambda (x) x)))
(set! num (+ num 1))
(k (lambda (x) x))
num

and

(begin
(define num 0)
(define k (call-with-current-continuation (lambda (x) x)))
(set! num (+ num 1))
(k (lambda (x) x))
num)

Try them in your favorite Scheme interpreter's REPL and you'll probably find that running the first yields 1 and the second yields 2. I tried it in mzscheme, guile, MIT Scheme, and Bigloo (the four Scheme interpreters I've currently got installed on my work computer) and that was the result in every case.

The only difference between the two pieces of code is that one's in a top-level begin and the other isn't, so clearly the R5RS specification that the two programs be equivalent isn't being followed. What's going on here?

What's going on here is that call/cc is exposing an implementation detail, as it does with so many features. My advisor suggests that the simplest way to implement a REPL will have a single evaluation context (i.e., what gets grabbed by call/cc) for every top-level expression it evaluates, and that the code that implements top-level begin uses that same continuation for all subexpressions rather than making a new one for each, meaning that the REPL's prompt is actually a prompt in the control-delimiting sense in all cases. Of course in that case you'll get the behavior I observed.

That behavior seems fine to me; in fact, considering that every Scheme implementation I tested does it this way it's probably a better way to do it than the way the R5RS suggests.

0 Comments:

Post a Comment

<< Home