My question is about function arguments in conjunction with continuations. Specifically, what behavior is required, and what is allowed.
Suppose you have a function call (f arg1 arg2 arg3). I realize that
a compliant Scheme implementation is allowed to evaluate the arguments
arg1, arg2, and arg3 in any order. That's fine. But now suppose
that, say, arg2 creates a continuation. In general, some of the other
arguments may be evaluated before arg2 is evaluated, and some may be
evaluated after arg2 is evaluated.
Suppose that, in the Scheme implementation we're using, arg1 is
evaluated before arg2. Further, suppose that f modifies its local
copy of the first argument. Later, when the continuation created
during the evaluation of arg2 is called, arg3 will be evaluated
again and f will be called.
The question is this: When f is called a second time, via the
continuation, what value must/may its first argument have? Does it
need to be the same value that arg1 evaluated to? Or may it be the
modified value from the previous call to f? (Again, this example
assumes that arg1 is evaluated before arg2, but the same issue
applies with different argument evaluation orders. I.e., if arg3 is
evaluated before arg2, then the question applies to arg3.)
I have tried this in a couple of Scheme implementations, and have obtained
differing results. I took into account different orders of evaluation of
the arguments (it's easy to track it by having the argument expressions
log when they're being evaluated). Ignoring that difference, one
implementation always used the original argument values, and another
sometimes used the original argument values, and sometimes used the
modified argument values, depending on whether f was an inline
lambda vs. a global function. Presumably the difference is due to
whether the actual arguments end up being copied into the function's
local variables, or whether they are used in-place.
Here is a version that uses a global function:
(define (bar x cc y)
(set! x (* x 2))
(set! y (* y 3))
(format #t "~a ~a\n" x y)
cc)
(define (foo a b)
(let* ((first #t)
(cb (bar
(+ a 10)
(call/cc (lambda (x) x))
(+ b 100))))
(if first
(begin
(set! first #f)
cb)
(cb '()))))
(define cc (foo 1 2))
(call/cc cc)
(call/cc cc)
The above version uses the original argument values when calling
the function bar in both of the Scheme implementations that I tested.
The function bar sees 11 for the first argument and 102 for the
third argument each time it is called. The output is:
22 306
22 306
22 306
Now, here is a version that replaces the global function with an inline lambda:
(define (foo a b)
(let* ((first #t)
(cb ((lambda (x cc y)
(set! x (* x 2))
(set! y (* y 3))
(format #t "~a ~a\n" x y)
cc)
(+ a 10)
(call/cc (lambda (x) x))
(+ b 100))))
(if first
(begin
(set! first #f)
cb)
(cb '()))))
(define cc (foo 1 2))
(call/cc cc)
(call/cc cc)
In one of the Scheme implementations I tested (BiwaScheme), this behaves the same as the previous version. I.e., the called function always sees the original argument values.
In another Scheme implementation (Gosh/Gauche), this behaves
differently from the previous version. In this case, the called
function uses the modified value of the first argument. In other
words, it handles the inline lambda differently, taking advantage of
the fact that it can see the function definition, and is presumably
using a more direct argument passing mechanism that avoids having to
copy them. Since it isn't copying the arguments, the ones that were
evaluated before the continuation point retain their modified values.
The lambda sees 11 and 102 for the first and third arguments the
first time, then it sees 22 and 102 the second time, and 44 and
102 the third time. So the continuation is picking up the modified
argument values. The output is:
22 306
44 306
88 306
So again, my question is this: Are both behaviors allowed by the Scheme standard (R6RS and/or R7RS)? Or does Scheme in fact require that the original argument values be used when the continuation is invoked?
Update: I originally reported that the Gauche Scheme implementation gave the three different sets of values shown above. That was true, but only for certain versions of Gauche. The version I originally tested was Gauche 0.9.3.3, which shows the three different sets of values. I later found a site that has three different versions of Gauche. The oldest, Gauche 0.9.4, also shows the three different sets of values. But the two newer versions, Gauche 0.9.5 and Gauche 0.9.8, both show the repeated values:
22 306
22 306
22 306
This argues pretty strongly that this was considered a bug which has since been fixed (just as everyone has been saying).