Suppose I have a predicate foo/2
which defines a relation between its first and second argument.
What is the most idiomatic and efficient way to change the implementation of foo/2
such that:
if both of its arguments are ground, it acts as before (succeeds if the relation holds, fails otherwise).
if one of the two arguments (or both) are free, it "constrains" those two arguments so that when they will get grounded, the relation will be checked.
In other words, how to correctly implement the behaviour exhibited by dif/2
but with any kind of user-defined relation?
listing(dif/2).
was of little help.
Different Prolog implementations provide different features to accomplish this. The mechanism is variously known as coroutining, delayed goals, constraints, and your Prolog system's manual will provide more information.
Here are two variants, which are available in SICStus Prolog and also some other systems.
block/1
directiveIn SICStus Prolog (and possibly some other systems), one way to lift a user-defined predicate to such a constrained version is available via the declarative block
declaration.
Interestingly, this does not require any changes to the predicate itself!
Suppose you have an impure version of dif/2
, using the non-monotonic (\=)/2
predicate:
madif(X, Y) :- X \= Y.
Then you can turn it into a delayed version for example with:
:- block madif(-, ?), madif(?, -). madif(X, Y) :- X \= Y.
Sample queries and answers:
| ?- madif(a, b). yes | ?- madif(a, X). user:madif(a,X) ? ; no | ?- madif(a, X), X = b. X = b ? ; no | ?- madif(X, Y). user:madif(X,Y) ? ; no
As required, the evaluation of the goal is delayed until both arguments are instantiated.
when/2
A second way to accomplish this with SICStus Prolog (and other systems that provide this feature) is to use when/2
. This requires changes to the predicate itself.
For example, using when/2
, you can implement madif/2
like this:
madif(X, Y) :- when((ground(X), ground(Y)), X \= Y).
Sample queries and answers:
| ?- madif(X, a). prolog:trig_ground(X,[],[X],_A,_A), prolog:when(_A,(ground(X),ground(a)),user:(X\=a)) ? ; no | ?- madif(X, a), X = b. X = b ? ; no