2

So

?- canCall(mary, Person).

works and terminates and

?- canFind(mary, Person).

also works and terminates. But somehow

?- canCall(mary, Person), canFind(mary, Person). 

does not terminate. What may be a possible reason?

Guy Coder
  • 24,501
  • 8
  • 71
  • 136
absolutelydevastated
  • 1,657
  • 1
  • 11
  • 28
  • Do `canCall(mary, Person).` and `canFind(mary, Person).` individually terminate even if you tell Prolog to find *all* solutions? – lurker Apr 04 '16 at 16:36
  • Do they terminate with `Person` unified to the same value? – SQB Apr 05 '16 at 06:16
  • @lurker They do. @SQB They give multiple answers each, and there are matches. Isn't it normal that `Person` does not unify to the same value anyway? – absolutelydevastated Apr 06 '16 at 21:16

2 Answers2

6

(What you actually meant was: Queries terminate individually, but their conjunction does not terminate, sometimes)

You have discovered here a very fundamental aspect of Prolog's termination properties. Let's see this with the following pure1 program:

canFind(mary, john).

canCall(mary, bob).
canCall(A, B) :-
   canCall(B, A).

?- canCall(mary, Person).
   Person = bob
;  ... .

?- canFind(mary, Person).
   Person = john.

Everything looks fine! Let's check this code in, such that everyone can use it. Now your hapless colleague tries:

?- canCall(mary, Person), canFind(mary, Person).
   loops.

Oh no, this loops! Maybe I just have to reorder the goals:

?- canFind(mary, Person), canCall(mary, Person).
   loops.

Again!

Of course, you are upset too. After all, you diligently tested this code. And it terminated. Or did it?

Two notions of termination

This is one of the most confusing things in Prolog: We have here (at least) two different notions of termination of a query. The one that you tested is (sometimes) called existential termination. However, I rather recommend to call this simply finds an answer. It is extremely brittle as you have experienced.

And if a query not only finds an answer but all of them and finishes the query, then this is called universal termination or simply termination. If Prolog programmers say that a query terminates, they mean that it terminates universally.

So how can we observe universal termination? Simply ask for all answers. In GNU-Prolog you type a. In other systems you will have to hammer SPACE or ;Return until it's finished, or your fatigued eyes or your carpal tunnels stop it.

?- canCall(mary, Person).
   Person = bob
;  Person = bob
;  Person = bob
;  Person = bob
;  Person = bob
;  ... .

So here we see that there are infinitely many answers (actually, we finite beings would have to prove that, but believe me for the moment).

Isn't there a cheaper way to observe this? Without all this walls of text of answers? You can "turn off" the answers by adding a condition that will never hold, false.

So ask instead:

?- canCall(mary, Person), false.

What can be the outcome of such a query? It can never be true. It can only be false, should it terminate. So with this query we simply test the termination property of a program only.

Now, the conjunction of two (universally) terminating queries will always terminate. So this kind of termination is much more robust.

There are many more cool properties of universal termination. For example, you can exchange the order of clauses (that is, facts and rules) as you like: No matter what order they are in, all programs share exactly the same termination property.

Another is that you can easily locate the source of non-termination in your programs with the help of a . Start reading with this one.

In command-oriented programming languages this notion is not readily present. However, with iterators, you have quite similar notions: If the iterator produces the first item, that would correspond to existential termination and if finitely many items are produced, that is, if after finitely many next it's finished, that would correspond to universal termination. Kind of.


1 Actually, in impure programs you have all kinds of nonsensical behavior. So it is pointless to consider them.

false
  • 10,264
  • 13
  • 101
  • 209
  • That's what's interesting, because both these queries terminate universally individually. So I get a list of answers when I press `;` and it stops at `false.` I'm thinking another problem contributes to this as well. It's that when I run `canCall(mary, Person).` it gives a list of answers like `bob; david; false.` But it goes into an infinite loop when I run `canCall(mary, bob).` I guess that's why it does not unify, but why does it not produce `true.` as an answer? – absolutelydevastated Apr 06 '16 at 21:08
  • 1
    @absolutelydevastated: No! `canCall/2` as I have defined it above, *never* terminates universally – false Apr 06 '16 at 22:28
  • Well "And if a query not only finds an answer but all of them and finishes the query, then this is called universal termination or simply termination." In my case, `canCall(mary, Person), false.` terminates so according to your definition it terminates universally. No? – absolutelydevastated Apr 06 '16 at 22:41
  • 1
    @absolutelydevastated: If `canCall(mary, Person), false.` terminates, then this is universal termination. But with my definition above, it does not terminate! – false Apr 06 '16 at 23:30
  • So in my case, there is universal termination, but a conjunction of both these clauses still doesn't work. – absolutelydevastated Apr 06 '16 at 23:36
  • @absolutelydevastated: That should not be the case. You really should show your code. Maybe it's impure. But otherwise, for pure programs it holds: that the conjunction of two terminating queries always terminates. – false Apr 06 '16 at 23:38
  • @absolutelydevastated: Add your actual code to your question! – false Apr 06 '16 at 23:39
0

I guess, when You run individually, Person unifies to two different values. Check this.

whd
  • 1,819
  • 1
  • 21
  • 52
  • 1
    Most definitely! But IMHO that is only a piece of the puzzle. How about expanding your answer? – repeat Apr 05 '16 at 09:00