prologprolog-diflogical-purity

prolog doesn't give me a solution when one exists


I am working through Seven Languages in Seven Weeks, but there is something I don't understand about prolog. I have the following program (based on their wallace and grommit program):

/* teams.pl */

onTeam(a, aTeam).
onTeam(b, aTeam).
onTeam(b, superTeam).
onTeam(c, superTeam).

teamMate(X, Y) :- \+(X = Y), onTeam(X, Z), onTeam(Y, Z).

and load it like this

?- ['teams.pl'].
true.

but it doesn't give me any solutions to the following

?- teamMate(a, X).
false.

it can solve simpler stuff (which is shown in the book):

?- onTeam(b, X).
X = aTeam ;
X = superTeam.

and there are solutions:

?- teamMate(a, b).
true ;
false.

What am I missing? I have tried with both gnu prolog and swipl.

...AND THERE IS MORE...

when you move the "can't be your own teammate" restriction to then end:

/* teams.pl */

onTeam(a, aTeam).
onTeam(b, aTeam).
onTeam(b, superTeam).
onTeam(c, superTeam).

teamMate(X, Y) :- onTeam(X, Z), onTeam(Y, Z), \+(X = Y).

it gives me the solutions I would expect:

?- ['teams.pl'].
true.

?- teamMate(a, X).
X = b.

?- teamMate(b, X).
X = a ;
X = c.

What gives?


Solution

  • The answer by mat gives you some high-level considerations and a solution. My answer is a more about the underlying reasons, which might or might not be interesting to you.

    (By the way, while learning Prolog, I asked pretty much the same question and got a very similar answer by the same user. Great.)

    The proof tree

    You have a question:

    Are two players team mates?

    To get an answer from Prolog, you formulate a query:

    ?- team_mate(X, Y).
    

    where both X and Y can be free variables or bound.

    Based on your database of predicates (facts and rules), Prolog tries to find a proof and gives you solutions. Prolog searches for a proof by doing a depth-first traversal of a proof tree.

    In your first implementation, \+ (X = Y) comes before anything else, so it at the root node of the tree, and will be evaluated before the following goals. And if either X or Y is a free variable, X = Y must succeed, which means that \+ (X = Y) must fail. So the query must fail.

    On the other hand, if either X or Y is a free variable, dif(X, Y) will succeed, but a later attempt to unify them with each other must fail. At that point, Prolog will have to look for a proof down another branch of the proof tree, if there are any left.

    (With the proof tree in mind, try to figure out a way of implementing dif/2: do you think it is possible without either a) adding some kind of state to the arguments of dif/2 or b) changing the resolution strategy?)

    And finally, if you put \+ (X = Y) at the very end, and take care that both X and Y are ground by the time it is evaluated, then the unification becomes more like a simple comparison, and it can fail, so that the negation can succeed.