Abscond is really jut a crude hack: it's exactly like a dynamic block return in that it has a block symbol as its target. It finds the dynamic exist point by walking the dynamic frames looking for the block type with the right symbol. Only when it finds it, it simply jumps there without doing any unwinding.
Jumping without unwinding is more primitive than with; e.g. C setjmp/lonjmp is more primitive than exceptions, right?
It required a shift in attitude: in a Lisp with unwind-protect, we regard the unwinding as a Holy Cow. A dynamic control transfer guarantees that all unwinds are performed on penalty of the run-time implementor being struck by lightning.
I had to convince myself that this heresy was beneficial.
It's like threads. When the system switches from one thread to another, it doesn't unwind the resources of the thread left behind! With continuations, you can simulate coroutines, and as you go between them, you can't be unwinding and re-establishing resources. Not all resources can work that way. E.g. say there is a local variable holding a socket connection. You don't want to tear down and re-connect.
Stuff has to stay intact.
So how do you use the "abscond"? For instance to implement yield. yield works with obtain. obtain creates a dynamic block that is captured as a function. That function returns the yielded items. To continue the computation in the obtain block, you call the function again, to step it to the next yield. You can pass it an argument, which pops out of its yield call.
This yield back and forth is facilitated by the "abscond" dynamic control transfers which don't unwind.
The neat thing is that if, say, the yield process sis a recursive function with unwind protects, they all happen in its context undisturbed by the yield exchanges; it's like an exchange of virtual particles through a quantum tunnel.
I think I got a bit confused by terminology. By unwinding, do you mean stuff like Scheme's dynamic-wind which is supposed to free and reacquire resources over the course of non-local jumps?
In the "against call/cc" article, it's said that dynamic-wind condemns call/cc by its very existence. Undelimited continuations leak memory and resources and thus this thing was created to free them on exit and reacquire them on reentry. I concluded that delimited continuations have less need of it due to their, well, delimited nature.
> It finds the dynamic exist point by walking the dynamic frames looking for the block type with the right symbol. Only when it finds it, it simply jumps there without doing any unwinding.
That's essentially what lone is doing. It doesn't free any resources when control is transferred. It just copies stack frames to a heap allocated continuation value, then unwinds the stack to the frame introduced by the control primitive.
By unwinding I mean performing registered clean-ups associated with dynamic controus (e.g. such as that set up by unwind-protect), whose effect is understood to be permanent, on the hypothesis that the contexts being abandoned cannot be returned to.
Scheme's dynamic-wind is a kind of academic attempt to repair uwinding for situations when contexts can be returned to.
It doesn't pass my smell test. If we are following a control flow that leaves a context in such a way that there is a possibility to resume there, we should simply. not touch anything.
On this topic, I didn't mention dynamically scoped variables. Continuations capture their dynamic context. So that is to say, suppose *print-base* is set to 16 for printing integers in hex. Or ruppose *stdout* is redirected to a file. What happens if we capture a delimited continuation, and then resume it?
Part of the resumption will be the reinstatement of the dynamic scope, so those variables are effectively captured. And I think that the capture of the dynamic scope goes beyond the prompt; i.e. it doesn't obey the delimiting, which is probably the least surprising requirement.
> That's essentially what lone is doing.
So that is good and you can think of unwinding (e.g. in an exception-related permanent control transfer) as separate from that.
> By unwinding I mean performing registered clean-ups
I understand now. By unwinding I meant simply moving the stack pointer backwards. I never considered that the word could carry more meaning than that.
> If we are following a control flow that leaves a context in such a way that there is a possibility to resume there, we should simply. not touch anything.
Agreed. The dynamic-wind situation seems somewhat insane. I hope to avoid it altogether.
This weekend I'm planning to work on more control flow primitives, including a lightweight exception mechanism which doesn't capture the continuation. I think a finally/ensure function will be useful for cleanup in the case of non-resumable exceptions.
> dynamically scoped variables
My language doesn't have them. It's very much like Scheme with its nested environments.
Jumping without unwinding is more primitive than with; e.g. C setjmp/lonjmp is more primitive than exceptions, right?
It required a shift in attitude: in a Lisp with unwind-protect, we regard the unwinding as a Holy Cow. A dynamic control transfer guarantees that all unwinds are performed on penalty of the run-time implementor being struck by lightning.
I had to convince myself that this heresy was beneficial.
It's like threads. When the system switches from one thread to another, it doesn't unwind the resources of the thread left behind! With continuations, you can simulate coroutines, and as you go between them, you can't be unwinding and re-establishing resources. Not all resources can work that way. E.g. say there is a local variable holding a socket connection. You don't want to tear down and re-connect.
Stuff has to stay intact.
So how do you use the "abscond"? For instance to implement yield. yield works with obtain. obtain creates a dynamic block that is captured as a function. That function returns the yielded items. To continue the computation in the obtain block, you call the function again, to step it to the next yield. You can pass it an argument, which pops out of its yield call.
This yield back and forth is facilitated by the "abscond" dynamic control transfers which don't unwind.
The neat thing is that if, say, the yield process sis a recursive function with unwind protects, they all happen in its context undisturbed by the yield exchanges; it's like an exchange of virtual particles through a quantum tunnel.