I was thinking: Implementing tail calls seems easy; the normal calling sequence of "do some setup, then jump" just turns into "don't bother with (most of) the setup, just jump". That is, don't move a new register base-pointer into place, etc.

But there's one wiggle: If you've created a continuation previously (and it still exists), then any call has to preserve the frame--you basically can't do a tail call, with its key implication of the "current frame" vaporizing, or being re-used (depending on how you want to describe it).

But that's not too much of a problem, with the following:

1) Consider a tailcall op a recommendation--but have the VM do a regular call, if necessary.
2) Regular calls create continuations, so you can't do a tail call out of a function, if you've already done a regular call inside that function, _unless_ we have an (efficient) way to tell if any such continuation was "saved". You can figure some of that out at compile-time (whether a regular call could have been already made), but you'd need runtime checks for other cases, unless you just forego a tail call any time you _could_ have already done a regular call (which avoids the runtime checks, but allows less actual tail calls).


Do any existing languages have both tail calls and continuations? Scheme mandates that anything which looks like a tail call, must be optimized as expected (other languages like Lisp merely permit it), but I don't know of Scheme having continuations. Scheme cares, of course, so that you can have ostensibly unlimited recursion, without running out of stack space (or really, memory). Tail calls and continuations seem a bit like opposites--one preserves state, the other destroys it.

Just thought I'd send out these thoughts, since the topic was mentioned recently.

JEff



Reply via email to