Maybe return-from is what you need?
Future versions of cl-cont should provide transformable versions of
commonly used built-in functions, but this hasn't been a high
priority.
> (with-call/cc
> (let/cc k
> (let ((i 0))
> (mapcar (lambda (x)
> (when (> i 1)
> (funcall k t))
> (format t "~D ~S~%" (incf i) x))
> '(a b c d)))))
>
> Output:
>
> 1 A
> 2 B
> 3 C
> 4 D
> (NIL NIL NIL NIL)
(FUNCALL k t) calls the rest of the computation (which is essentially
empty), returns, FORMAT returns nil, and MAPCAR continues to execute.
You should really treat this as undefined behavior because MAPCAR
isn't transformed here. If you use LOOP, or write your own version of
MAPCAR, it will work as you expect.
> In my understanding of escaping continuations control flow is passed
> at FUNCALL, which calls the function that models the rest of the
> (delimited) computation.
That's correct.
> I suppose (and here I'm already treading on
> thin ice) the FUNCALL returns normally
Yes, this is correct.
> (with what result? That of the
> rest of the delimited computation?)
Funcall invokes the rest of the computation, and simply returns
whatever value it computes.
> the lambda/mapcar resumes its normal course of operation.
Well, if this were to work correctly, only the first element of the
list would be processed. Funcall would then transfer control to to the
rest of the continuation (which in this case is empty), and true would
be returned. Actually, in cl-cont, the return value of format would be
returned, not true, because calling the rest of the computation via
funcall doesn't make everything after funcall unreachable. But again,
this doesn't work as expected because MAPCAR isn't transformed.
> Is there a way to make the call of the continuation behave like a true
> escape, discarding all that follows its call?
You mean ignore the code after funcall/or funcall automatically? One
would to do that would be to set up catch blocks and have cl-cont
automatically throw to them, but this generally isn't a good idea.
There was a post on this somewhere by Bill Clementson regarding Arnesi
library. Basically, delimited continuations don't capture the whole
computation, so if you transfer control like you do in a real
continuation you'll have no way to implement interesting things (like
threads/processes, coroutines, etc) since unlike in scheme, once you
invoke the delimited continuation the old computation doesn't get
thrown away.
Hope this helps. It can be quite tricky to understand, and is
unfortunately tricky to explain. Also, I need to refresh my memory
every time I look at this, somehow this refuses to just settle down
properly in my mind :)