Do they exist? How are they implemented?
The coroutining predicates of SWI-Prolog (freeze
, when
, dif
etc.) have the functionality of guards. How do they fit in the preferred Prolog programming style?
I am very new to logic programming (with Prolog and altogether) and somewhat confused by the fact that it is not purely declarative, and requires procedural considerations even in very simple cases (see this question about using \==
or dif
). Am I missing something important?
First a terminological issue: Neither
freeze/2
norwhen/2
nordif/2
are called guards under any circumstance. Guards appear in such extensions as CHR, or related languages as GHC (link in Japanese) or other Concurrent logic programming languages ; you even (under certain restrictions) might consider clauses of the formas clauses containing a guard and the cut would be in this case rather called a commit. But none applies to above primitives. Guards are rather inspired by Dijkstra's Guarded Command Language of 1975.
freeze(X, Goal)
(originally calledgeler
) is the same aswhen(nonvar(X), Goal)
and they both are declaratively equivalent toGoal
. There is no direct relation to the functionality of guards. However, when used together with if-then-else you might implement such a guard. But that is pretty different.freeze/2
and similar constructs were for some time considered as a general way to improve Prolog's execution mechanism. However, they turned out to be very brittle to use. Often, they were too conservative thus delaying goals unnecessarily. That is, almost every interesting query produced a "floundering" answer as the query below. Also, the borderline between terminating and non-terminating programs is now much more complex. For pure monotonic Prolog programs that terminate, adding some terminating goal into the program will preserve termination of the entire program. However, withfreeze/2
this is no longer the case. Then from a conceptual viewpoint,freeze/2
was not very well supported by the toplevels of systems: Only a few systems showed the delayed goals in a comprehensive manner (e.g. SICStus) which is crucial to understand the difference between success/answers and solution. With delayed goals Prolog may now produce an answer that has no solution as this one:Another difficulty with
freeze/2
was that termination conditions are much more difficult to determine. So, whilefreeze
was supposed to solve all the problems with termination, it often created new problems.And there are also more technical difficulties related to
freeze/2
in particular w.r.t tabling and other techniques to prevent loops. Consider a goalfreeze(X, Y = 1)
clearly,Y
is now1
even if it is not yet bound, it still awaitsX
to be bound first. Now, an implementation might consider tabling for a goalg(Y)
.g(Y)
will now have either no solution or exactly one solutionY = 1
. This result would now be stored as the only solution forg/1
since thefreeze
-goal was not directly visible to the goal.It is for such reasons that
freeze/2
is considered the goto of constraint logic programming.Another issue is
dif/2
which today is considered a constraint. In contrast tofreeze/2
and the other coroutining primitives, constraints are much better able to manage consistency and also maintain much better termination properties. This is primarily due to the fact that constraints introduce a well defined language were concrete properties can be proven and specific algorithms have been developed and do not permit general goals. However, even for them it is possible to obtain answers that are not solutions. More about answer and success in CLP.