When discussing various versions of the same program, it helps a lot to rename those versions such that they can cohabit in the same program:
sumA(0, Y, Y).
sumA(s(X), Y, s(Z)) :- sumA(X, Y, Z).
sumB(0, Y, Y).
sumB(s(X), Y, s(Z)) :- sumB(Y, X, Z).
?- sumA(X, 0, Z).
X = 0, Z = 0
; X = s(0), Z = s(0)
; X = s(s(0)), Z = s(s(0))
; X = s(s(s(0))), Z = s(s(s(0)))
; ... .
?- sumB(X, 0, Z).
X = 0, Z = 0
; X = s(_A), Z = s(_A).
(Above output is from Scryer, which is better readable)
Before looking at the concrete definitions, think of the set of solutions that we expect for X
and Z
. Both should be the same, and they should describe all natural s(X)-numbers 0
, s(0)
, s(s(0))
... So we have infinitely many solutions. How shall we, finite beings with our finite Prolog systems, describe infinitely many solutions? Well, we can give it a try and just start1. That's what we see in sumA
. What is nice here, is that each and every answer is actually a solution (that is, there are no variables remaining2). Just a pity that the program does not terminate (universally). At least it produces all solutions in a fair manner. Wait long enough and also your favorite solution3 will appear.
In sumB
that infinite set of natural numbers is enumerated in a quite different manner. First, we get a solution for 0
and then we get a single answer which includes all other numbers ≥ 1. So we have compressed this infinite set into a single variable! That's the power of logic variables. Of course, we paid a tiny price here, for now we also include really everything in the argument of s(_A). Everything, including s(nothing)
.
?- Z = s(nothing), sumA(Z, 0, Z).
false.
?- Z = s(nothing), sumB(Z, 0, Z).
Z = s(nothing).
Which of these two rules is correct?
Both sumA
and sumB
are correct. And sumB
as long as we can distinguish our numbers from other terms (that's what roughly corresponds to a well typed program).
But which one is preferable? It all depends. Usually the predicate will be used in some context. Maybe a subsequent goal will only terminate if one of its arguments is ground. In this case, sumA
may be preferable. If there is no further goal then sumB
is always preferable. It all depends a bit.
What is most important to see is that some of the infinities of solutions can be elegantly subsumed with logic variables thereby improving the termination property. For more complex situations this will not be enough but then constraints will be attached to those variables.
1) And don't be upset if we get a resource_error(memory)
, it's just a finite system.
2) And no residual constraints to be precise.
3) My favorite numbers are 7^7^7 and 9^9^9. With current implementations this will take some time and space.
2 unresolved: [sum(b,f,f),sum(f,b,f)].
! So again this is a weakness of cTI. By unfolding the definition once you get the optimal result which is sump(A,B,C)terminates_if b(A);b(B);b(C). Voir. – Rooseveltroost