I have been wondering why there is such an asymetry between “specifying a program at database level” and “specifying a program as a term” (as a goal, really). Once you notice it, it’s weird!
Code and unit tests code are here
Consider this program in “Prolog database form”:
foo(a(K)) :-
debug(foo,"Database form: a(K) with K = ~q",[K]),
(var(K) -> K= 666 ; true).
foo(b(K)) :-
debug(foo,"Database form: b(K) with K = ~q",[K]),
(var(K) -> K= 999 ; true).
The above can be alternatively expressed in “goal form”. Note that the goal form is a conjunction, where what was previously the head is now just an explicit unification at first position. There is no implication (and definitely no ‘->’ control construct) Proving X :- Y
is the same as running X,Y
.
goalform(X) :-
G =
((X = a(K),
debug(foo,"Goal form: a(K) with K = ~q",[K]),
(var(K) -> K=666 ; true));
(X = b(K),
debug(foo,"Goal form: b(K) with K = ~q",[K]),
(var(K) -> K=999 ; true))),
call(G).
In “database form” the head has more “power” - simultaneous unification of several arguments, efficient lookup etc … why not have Goals which have :-
too (they exist more or less in Lambda Prolog but the theoretical underpinning are unclear to me).
Anyway, the above lead to trying out library(yall)
on the goalform. And there is a little problem. Consider:
goalform_yall_bad(X) :-
K = as, % the andromeda strain
G =
({}/[X]>>
((X = a(K),
debug(foo,"Goal form: a(K) with K = ~q",[K]),
(var(K) -> K=666 ; true));
(X = b(K),
debug(foo,"Goal form: b(K) with K = ~q",[K]),
(var(K) -> K=999 ; true)))),
call(G,X).
The above works the same way as the database form, as long as K
stays fresh. Once K
is instantiated, it bleeds into the goalform, wrecking results (see the test cases here). This in spite of explicitly stating via {}
that “no free variables inside the lambda-enclosed term are connected to outside”.
Is it possible to fix that?