[GvR]
> ...
> Perhaps it would help to explain what a continuation actually does
> with the run-time environment, instead of giving examples of how to
> use them and what the result it?
Paul Wilson (the GC guy) has a very nice-- but incomplete --intro to Scheme
and its implementation:
ftp://ftp.cs.utexas.edu/pub/garbage/cs345/schintro-v14/schintro_toc.html You can pick up a lot from that fast. Is Steven (Majewski) on this list?
He doped most of this out years ago.
> Here's a start of my own understanding (brief because I'm on a 28.8k
> connection which makes my ordinary typing habits in Emacs very
> painful).
>
> 1. All program state is somehow contained in a single execution stack.
> This includes globals (which are simply name bindings in the botton
> stack frame).
Better to think of name resolution following lexical links. Lexical
closures with indefinite extent are common in Scheme, so much so that name
resolution is (at least conceptually) best viewed as distinct from execution
stacks.
Here's a key: continuations are entirely about capturing control flow
state, and nothing about capturing binding or data state. Indeed, mutating
bindings and/or non-local data are the ways distinct invocations of a
continuation communicate with each other, and for this reason true
functional languages generally don't support continuations of the call/cc
flavor.
> It also includes a code pointer for each stack frame indicating where
> the function corresponding to that stack frame is executing (this is
> the return address if there is a newer stack frame, or the current
> instruction for the newest frame).
Yes, although the return address is one piece of information in the current
frame's continuation object -- continuations are used internally for
"regular calls" too. When a function returns, it passes control thru its
continuation object. That process restores-- from the continuation
object --what the caller needs to know (in concept: a pointer to *its*
continuation object, its PC, its name-resolution chain pointer, and its
local eval stack).
Another key point: a continuation object is immutable.
> 2. A continuation does something equivalent to making a copy of the
> entire execution stack. This can probably be done lazily. There are
> probably lots of details.
The point of the above is to get across that for Scheme-calling-Scheme,
creating a continuation object copies just a small, fixed number of pointers
(the current continuation pointer, the current name-resolution chain
pointer, the PC), plus the local eval stack. This is for a "stackless"
interpreter that heap-allocates name-mapping and execution-frame and
continuation objects. Half the literature is devoted to optimizing one or
more of those away in special cases (e.g., for continuations provably
"up-level", using a stack + setjmp/longjmp instead).
> I also expect that Scheme's semantic model is different than Python
> here -- e.g. does it matter whether deep or shallow copies are made?
> I.e. are there mutable *objects* in Scheme? (I know there are mutable
> and immutable *name bindings* -- I think.)
Same as Python here; Scheme isn't a functional language; has mutable
bindings and mutable objects; any copies needed should be shallow, since
it's "a feature" that invoking a continuation doesn't restore bindings or
object values (see above re communication).
> 3. Calling a continuation probably makes the saved copy of the
> execution stack the current execution state; I presume there's also a
> way to pass an extra argument.
Right, except "stack" is the wrong mental model in the presence of
continuations; it's a general rooted graph (A calls B, B saves a
continuation pointing back to A, B goes on to call A, A saves a continuation
pointing back to B, etc). If the explicitly saved continuations are never
*invoked*, control will eventually pop back to the root of the graph, so in
that sense there's *a* stack implicit at any given moment.
> 4. Coroutines (which I *do* understand) are probably done by swapping
> between two (or more) continuations.
>
> 5. Other control constructs can be done by various manipulations of
> continuations. I presume that in many situations the saved
> continuation becomes the main control locus permanently, and the
> (previously) current stack is simply garbage-collected. Of course the
> lazy copy makes this efficient.
There's much less copying going on in Scheme-to-Scheme than you might think;
other than that, right on.
> If this all is close enough to the truth, I think that continuations
> involving C stack frames are definitely out -- as Tim Peters
> mentioned, you don't know what the stuff on the C stack of extensions
> refers to. (My guess would be that Scheme implementations assume that
> any pointers on the C stack point to Scheme objects, so that C stack
> frames can be copied and conservative GC can be used -- this will
> never happen in Python.)
"Scheme" has become a generic term covering dozens of implementations with
varying semantics, and a quick tour of the web suggests that cross-language
Schemes generally put severe restrictions on continuations across language
boundaries. Most popular seems to be to outlaw them by decree.
> Continuations involving only Python stack frames might be supported,
> if we can agree on the the sharing / copying semantics. This is where
> I don't know enough see questions at #2 above).
I'd like to go back to examples of what they'd be used for <wink> -- but
fully fleshed out. In the absence of Scheme's ubiquitous lexical closures
and "lambdaness" and syntax-extension facilities, I'm unsure they're going
to work out reasonably in Python practice; it's not enough that they can be
very useful in Scheme, and Sam is highly motivated to go to extremes here.
give-me-a-womb-and-i-still-won't-give-birth-ly y'rs - tim