Debugging using watch

We finish the section on the interpreter with a discussion on debugging. For debugging Prolog code we often use trace. This can be very frustrating as we will often sufer from information overload but, even worse, we might accidentally use skip when we should have continued to use creep causing us to start again.

In QuLog we have tried to make debugging simpler. Here is a relation definition in the qlexamples.qlg file.

rel only_has_adult_children(?human)
only_has_adult_children(P) <=
exists C child_of(C,P) &
forall C (
child_of(C, P) =>
exists A age_of(P, A) & A > 20)
It can be used for both finding the humans who have one of more children but all are above the age of 20, or for checking if a given human has that property.

We can watch its use by entering the command

| ?? watch only_has_adult_children.

success

If we do a show only_has_adult_children we will see the same single rule definition but when we query the relation we get information about its use.

| ?? only_has_adult_children(P).

1:only_has_adult_children(P)
Call 1 unifies rule 1
output none
Rule body is:
child_of(_, P) &
forall C_0 (
child_of(C_0, P) =>
exists A_0 age_of(P, A_0) & A_0 > 20)
1:only_has_adult_children(roger) succeeded
P = roger : man

1:only_has_adult_children(P) seeking another proof
1:only_has_adult_children(roger) succeeded
...
P = roger : man % Answer roger given again

1:only_has_adult_children(P) seeking another proof
1:only_has_adult_children(mary) succeeded
...
P = mary : woman

1:only_has_adult_children(P) seeking another proof
1:only_has_adult_children(bill) succeeded
...
P = bill : man

1:only_has_adult_children(P) seeking another proof
1:only_has_adult_children(rose) succeeded
...
P = rose : woman
.. % .. entered to request more answers if there are any

1:only_has_adult_children(P) seeking another proof
no (more) proofs using rule 1 trying next rule for call 1
1:only_has_adult_children(P) no (more) proofs
no more solutions

Ideally we would not get roger given as an answer twice. The suspician is that it might be multiple solutions to the test or generate condition child_of(_,P) giving P=roger twice. The use of the anonymous variable _ indicates we are not interested in knowing the child of P.

We can add a watch on the relation child_of and repeat the query:

watch child_of.
| ?? only_has_adult_children(P).

1:only_has_adult_children(P)
Call 1 unifies rule 1
output none
Rule body is:
child_of(_, P) &
forall C_0 (
child_of(C_0, P) =>
exists A_0 age_of(P, A_0) & A_0 > 20)
2:child_of(A, P)
Call 2 unifies rule 1
output P = roger A = tom
% First proof of child_of(A,P) with P=roger
No rule body
2:child_of(tom, roger) succeeded
3:child_of(C_0, roger)
Call 3 unifies rule 1
output C_0 = tom
No rule body
3:child_of(tom, roger) succeeded
3:child_of(C_0, roger) seeking another proof
no (more) proofs using rule 1 trying next rule for call 3
Call 3 unifies rule 2
output C_0 = june
No rule body
3:child_of(june, roger) succeeded
3:child_of(C_0, roger) seeking another proof
no (more) proofs using rule 2 trying next rule for call 3
3:child_of(C_0, roger) no (more) proofs
1:only_has_adult_children(roger) succeeded
P = roger : man

1:only_has_adult_children(P) seeking another proof
2:child_of(A, P) seeking another proof
no (more) proofs using rule 1 trying next rule for call 2
Call 3 unifies rule 2
output P = roger A = june
% Second proof of child_of(A,P) with P=roger again
No rule body
2:child_of(june, roger) succeeded
4:child_of(C_0, roger)
Call 4 unifies rule 1
output C_0 = tom
No rule body
4:child_of(tom, roger) succeeded
4:child_of(C_0, roger) seeking another proof
no (more) proofs using rule 1 trying next rule for call 4
Call 4 unifies rule 2
output C_0 = june
No rule body
4:child_of(june, roger) succeeded
4:child_of(C_0, roger) seeking another proof
no (more) proofs using rule 2 trying next rule for call 4
4:child_of(C_0, roger) no (more) proofs
1:only_has_adult_children(roger) succeeded
...
P = roger : man
.
.
.

To avoid the repeated answers for any parent that has more than 1 child we can change the definition of only_has_adult_children replacing the condition child_of(_,P) by the condition has_a_child(P) where

rel has_a_child(?human)
has_a_child(P) <=
isa(P,human) &
% When P needs to be found, generate in turn names of humans
once(child_of(_,P))
% Then check, once only, if they have a child

We now get the answer P=roger just once.

| ?? only_has_adult_children2(P).

P = roger : man
...
P = bill : man
...
P = mary : woman
...
P = rose : woman

As a final version of the definition for the relation only_has_adult_children we replace the use of the child_of condition in the forall check by a call to the function age.

fun age(human) -> age_val
age(P) :: age_of(P,A) -> A
age(_) -> 0
% 0 is used as the default age if none is recorded

rel only_has_adult_children3(?human)
only_has_adult_children3(P) <=
has_a_child(P) &
% This will generate one at a time all humans with at least one child
forall C (
child_of(C,P) => age(P) > 20
)

We can also watch a function evaluation.

watch age.

| ?? only_has_adult_children3(P).

1 : age(roger) (matches rule 1)
1 : age(roger) -> 110
1 : age(roger) <- 110
2 : age(roger) (matches rule 1)
2 : age(roger) -> 110
2 : age(roger) <- 110
P = roger : man
3 : age(bill) (matches rule 1)
3 : age(bill) -> 40
3 : age(bill) <- 40
...
P = bill : man
4 : age(mary) (matches rule 1)
4 : age(mary) -> 40
4 : age(mary) <- 40
...
P = mary : woman
5 : age(rose) (matches rule 1)
5 : age(rose) -> 40
5 : age(rose) <- 40
...
P = rose : woman