[Christian] [... clarified stuff ... thanks! ... much clearer ...]
... If a frame is the current state, I make it two frames to have two current states. One will be saved, the other will be run. This is what I call "splitting". Actually, splitting must occour whenever a frame can be reached twice, in order to keep elements alive.
That part doesn't compute: if a frame can be reached by more than one path, its refcount must be at least equal to the number of its immediate predecessors, and its refcount won't fall to 0 before it becomes unreachable. So while you may need to split stuff for *some* reasons, I can't see how keeping elements alive could be one of those reasons (unless you're zapping frame contents *before* the frame itself is garbage?).
... Well, I see. You want one locals and one globals, shared by two incarnations. Gets me into trouble.
Just clarifying what Scheme does. Since they've been doing this forever, I don't want to toss their semantics on a whim <wink>. It's at least a conceptual thing: why *should* locals follow different rules than globals? If Python2 grows lexical closures, the only thing special about today's "locals" is that they happen to be the first guys found on the search path. Conceptually, that's really all they are today too. Here's the clearest Scheme example I can dream up: (define k #f) (define (printi i) (display "i is ") (display i) (newline)) (define (test n) (let ((i n)) (printi i) (set! i (- i 1)) (printi i) (display "saving continuation") (newline) (call/cc (lambda (here) (set! k here))) (set! i (- i 1)) (printi i) (set! i (- i 1)) (printi i))) No loops, no recursive calls, just a straight chain of fiddle-a-local ops. Here's some output:
(test 5) i is 5 i is 4 saving continuation i is 3 i is 2 (k #f) i is 1 i is 0 (k #f) i is -1 i is -2 (k #f) i is -3 i is -4
So there's no question about what Scheme thinks is proper behavior here.
... Let me explain. What Python does right now is: When a function is invoked, all local variables are copied into fast_locals, well of course just references are copied and counts increased. These fast locals give a lot of speed today, we must have them.
Scheme (most of 'em, anyway) also resolves locals via straight base + offset indexing.
You are saying I have to share locals between frames. Besides that will be a reasonable slowdown, since an extra structure must be built and accessed indirectly (right now, i's all fast, living in the one frame buffer),
GETLOCAL and SETLOCAL simply index off of the fastlocals pointer; it doesn't
care where that points *to* I cannot say that I'm convinced that this is what we need. Suppose you have a function def f(x):
# do something
...
# in some context, wanna have a snapshot
global snapshot # initialized to None
if not snapshot:
snapshot = callcc.new()
# continue computation
x = x+1
... What I want to achieve is that I can run this again, from my
snapshot. But with shared locals, my parameter x of the
snapshot would have changed to x+1, which I don't find useful. You need a completely fleshed-out example to score points here: the use of
call/cc is subtle, hinging on details, and fragments ignore too much. If
you do want the same x,
commonx = x
if not snapshot:
# get the continuation
# continue computation
x = commonx
x = x+1
...
That is, it's easy to get it. But if you *do* want to see changes to the
locals (which is one way for those distinct continuation invocations to
*cooperate* in solving a task -- see below), but the implementation doesn't
allow for it, I don't know what you can do to worm around it short of making
x global too. But then different *top* level invocations of f will stomp on
that shared global, so that's not a solution either. Maybe forget functions
entirely and make everything a class method. I want to fix a state of the current frame and still think
it should "own" its locals. Globals are borrowed, anyway.
Class instances will anyway do what you want, since
the local "self" is a mutable object. How do you want to keep computations independent
when locals are shared? For me it's just easier to
implement and also to think with the shallow copy.
Otherwise, where is my private place?
Open for becoming convinced, of course :-) I imagine it comes up less often in Scheme because it has no loops:
communication among "iterations" is via function arguments or up-level
lexical vrbls.
So recall your uses of Icon generators instead: like Python, Icon does have
loops, and two-level scoping, and I routinely build loopy Icon generators
that keep state in locals. Here's a dirt-simple example I emailed to Sam
earlier this week:
procedure main()
every result := fib(0, 1) \ 10 do
write(result)
end
procedure fib(i, j)
local temp
repeat {
suspend i
temp := i + j
i := j
j := temp
}
end
which prints
0
1
1
2
3
5
8
13
21
34
If Icon restored the locals (i, j, temp) upon each fib resumption, it would
generate a zero followed by an infinite sequence of ones(!).
Think of a continuation as a *paused* computation (which it is) rather than
an *independent* one (which it isn't <wink>), and I think it gets darned
hard to argue.
theory-and-practice-agree-here-in-my-experience-ly y'rs - tim