Prolog passes all in,out,in/out values as arguments to the predicate. This has the drawback, for out variables, that the variables need to be allocated from the heap and adds complexity and overhead to the predicate. If in stead the out variables could be passed over without making a box for it first we get possible much less complexity of the predicate and hence better performance. So let's see the semantics that I plan to add to guile-log and already works today in a experimental compiler of an experimental prolog VM that is included in the guile-log sources. So the basic form is
% Executing a goal with out variables % unicode ← is indicating a return value, (leftarrow in latex) [V1, ..., Vn] ← Goal. % Executing a goal with named out variables placed where out may be % referenced by executing the goal 'F(...)' F(V1, ..., Vn) ← Goal. % an alternative default symbol is <=, likewise for rightarrow we have Goal → [V1, ..., Vn]. Goal → F(V1, ..., Vn). % lets make a predicate that outputs a pure out variable, cc(X,...) % exits to the prevoius `←` and return X,... to it. sum(X,Y) :- Z is X + Y, cc(Z). % and let's use it printsum(X,Y) :- [Z1] ← sum(X,Y), write(output(Z1)),nl. % we can also just inline code printsum(X,Y) :- [Z1] ← (X < Y -> (Q is X + Y, cc(Q)) ; cc(X)), write(output(Z1)),nl.
Now, it it is possible to compile ordinary old versions of prolog code to take advantage of this system. But I tend to like the clarity of the code where out variables are explicit.
A possible extention for this would be a
Tag(V1,...) version, e.g.
% we can also just inline code printsum(X,Y) :- A(Z1) ← (X < Y -> (Q is X + Y, cc(Q)) ; A(X)), write(output(Z1)),nl.
Then if we have multiple exit points then one can sellect the outer one with the correct tag. Note that this is very similar to
catch/throw in prolog. The taged one only works in explicit code, if tags needs to be catched from inside an evaluation of a predicate, then use
catch/throw. This system has it's value because the
catch/throw is pinned down to assume a predicate can indeed make a throw, but with the
ccc the predicates at tail position in the goal clause is the only one that allow a
cc/ccc-throw. Also a difference is that
cc is a continuation, hence the name, e.g. it is not a throw or return in which the bindings are undone as with throw catch.
So to summarize, this extension is only to enable more effective code and enable more opertunity for uses of the stack, ctach/throw is more generall and don't enable this oppertunity due to this.
?- [X] ← cc(1). X = 1. ?- [X] ← cc(1,2). no ?- [X,Y] ← cc(1,2). X = 1, Y = 2. ?- X ← cc(1,2). X = [1, 2]. ?- F(X) ← ((Y ← F(1)),(write(y(Y)),nl)) X = 1, F = lam. ?- F(|X) ← ((Y ← F(1)),(write(y(Y)),nl)) X = , F = lam. ?- F(A|X) ← ((Y ← F(1,2,3)),(write(y(Y)),nl)) A = 1, X = [2, 3], F = lam.