The main work has been to enable delimeted continuation in guile-log. To do this is not easy because we want to support interleaving, postponing, store/restore, zip like construct as well as acumulation all in one theoretically and computationally sound environment. It’s not just to store and restore a stack as in scheme. Guile log is targetted to be effective reinstating states and keep a huge number of states in memory, to do this we note 1. we store a tree of states to compress the overall memory need and keep consing to a lower degree. 2. reinstating state information multiple times is cheap because there is no consing the second time for the main stack (we keep another dynamic stack where we need to reinstate new datastructures at each reinstation. All this means that we must reinstate the same datastructure. Also generally the cc and p fields at the in and out of the generated continuation needs to be patched and in order to support all of guile-log seamlessly one need to make use of guarded variables. In all stackless dynamics like kanren employ is not well suited because this is a nonfunctional modification. The resulting continuation will work almost as one would expect, but one can imagine problematics if the evaluation of the continuation returns a failure thunk intended to jump back into the continuation without normal backtracking. This is not a serious flaw and we do not try to add functionality to support this more than that we would implement new versions of the frameworks, looking at that source code it would be clear how to proceed.
G.L. (<prompt> tag data thunk handler), This will evaluate the guile-log version of a thunk and continue just as with any other thunk. But if an abort with a tag that matches the prompt tag or if the prompt tag is true, any abort will be catched. data will be guile-log dynamic data that will be captured at the abort and transferd back to the prompts context. The handler is of the form
(handler tag next kk args ...),
tag is the abort tag, next is a guile-log thunk, that evaluated will try the next handler in the stack. kk is a meta continuation. typically to get the continuaiton
k, you do
(let ((k (kk))) ...)
But we can also do
(let ((k (kk new-handler))) ...), in cases where one want to use a new
handler for the continuation which is a common pattern in producing generators.
Technically this is not needed, but doing so means the difference between dog
slow code and performant code. Finally we can use,
(let ((k (kk tag new-handler))) ...), with the obvious interpretation. The return continuation k will have a few arguments that represents the arguments used in the continuation of the abort.
(args ...) are the arguments returned by the abort.
G.L. (<catch> tag data thunk handler) this is exactly the same interface
as with prompts, but with the difference that the handler has the form
(handler tag next args ...), This is the same as prompts, but does not have any continuation argument. This ideom has significantly lower overhead.
G.L. (<abort> tag lambda args ...), will look for a handler matching
the tag and then call the handler with
(args ...), see the doc for
<prompt>. The lambda should be a guile-log lambda and defines the signature that the continuation inside the handler have.
(use-modules (logic guile-log)) (use-modules (logic guile-log umatch)) (<define> (f) (<values> (i x) (<abort> 'tag (<lambda> (i x) (<cc> i x)))) (<or-i> (<or> (<=> x i) (<=> x ,(+ i i))) (<or> (<=> x ,(- i)) (<=> x ,(- (+ i i)))))) (<define> (test x y) (<prompt> 'tag #f f (<lambda> (tag next kk) (<let> ((k (kk))) (<zip> (x (k 1 x)) (y (k 10 y))))))) (<run> 10 (x y) (test x y)) --> $1 = ((1 10) (-1 -10) (2 20) (-2 -20))