Search code examples
prologprolog-findall

findall/3 incorrectly evaluating to false


I'm creating a program which should allow search through a graph, but the function which should return a list of successor nodes is failing when a call to findall/3 evaluates to false. When I try the findall function on its own outside of the find_successors function it works perfectly, but for some reason inside the find_successors function it simply reads false. Stepping through with the graphical debugger I can even see it finding all the solutions. Here's the code:

find_successors(Start, Out) :- 
    entity(Start),
    (findall(X, is_a(Start, X), O), append([], O, OL1); OL1 = []),
    (findall(X, is_a(X, Start), O), OL2 = O; OL2 = []),

    (findall(X, has(Start, X), O), append([], O, OL3); OL3 = []),
    (findall(X, has(X, Start), O), append([], O, OL4); OL4 = []),

    (findall(X, able_to(Start, X), O), append([], O, OL5); OL5 =[]),
    (findall(X, able_to(X, Start), O), append([], O, OL6); OL6 = []),

    (findall(X, used_to(Start, X), O), append([], O, OL7); OL7 = []),
    (findall(X, used_to(X, Start), O), append([], O, OL8); OL8 = []),

    append([OL1, OL2, OL3, OL4, OL5, OL6, OL7, OL8], Out).

entity(wings).
entity(fly).
entity(bird).
entity(legs).
entity(feathers).
entity('body covering').
entity(animal).
entity(dog).
entity(fur).
entity(aves).
entity(reptile).
entity(snake).
entity(scales).

f_is_a(bird, aves).
f_is_a(bird, animal).
f_is_a(snake, reptile).
f_is_a(snake, animal).
f_is_a(dog, mammal).
f_is_a(dog, animal).
f_is_a(feathers, 'body covering').
f_is_a(fur, 'body covering').
f_is_a(mammal, animal).
f_is_a(reptile, animal).
f_is_a(aves, animal).
is_a(X, H) :- !, f_is_a(X, H).
is_a(X, H) :- !, \+f_is_a(X, P), H = X.
is_a(X, H) :- !, is_a(X, P), is_a(P, H).

f_has(bird, wings).
f_has(bird, feathers).
f_has(bird, legs).
f_has(aves, wings).
f_has(aves, feathers).
f_has(aves, legs).
f_has(dog, legs).
f_has(dog, fur).
f_has(mammal, legs).
f_has(mammal, fur).
f_has(snake, scales).
f_has(reptile, scales).
has(X, H) :- !, f_has(X, H).
has(X, H) :- !, \+f_has(X, P), H = X.
has(X, H) :- !, has(X, P), has(P, H).

used_to(wings, fly).
used_to(legs, walk).

able_to(bird, fly).
able_to(bird, walk).
able_to(dog, walk).
able_to(X, Y) :- used_to(X1, Y), has(X, X1).

Solution

  • You keep on trying to reuse the same variable, but once a variable is bound, you can't use it again. So all these:

            here              here
             |                  |
             v                  v
    (findall(X, is_a(Start, X), O), append([], O, OL1); OL1 = []),
    (findall(X, is_a(X, Start), O), OL2 = O; OL2 = []),
    
    (findall(X, has(Start, X), O), append([], O, OL3); OL3 = []),
    (findall(X, has(X, Start), O), append([], O, OL4); OL4 = []),
    
    (findall(X, able_to(Start, X), O), append([], O, OL5); OL5 =[]),
    (findall(X, able_to(X, Start), O), append([], O, OL6); OL6 = []),
    
    (findall(X, used_to(Start, X), O), append([], O, OL7); OL7 = []),
    (findall(X, used_to(X, Start), O), append([], O, OL8); OL8 = []),
    

    And every single one of these lines is very, very strange. I need to break it down to actually figure out what is going on. Taking just one of these:

    (   findall(X, used_to(Start, X), O),
        append([], O, OL7)
    ;   OL7 = []
    )
    

    (this, btw, is how you should try to write disjunctions, otherwise they are easy to misread)

    append([], A, B) is just the same as A = B.

    Then, findall/3 always succeeds, even if there are no solutions; it just gives you an empty list!

    ?- findall(X, between(2, 1, X), Xs).
    Xs = [].
    

    So the whole thing is completely unnecessary, you can just as well throw away everything but the call to findall/3.

    Note on the side: the disjunction you are using does not do what you think it does. Here is a small example:

    ?- ( A = 1 ; A = 2 ).
    

    What do you think happens?