Here is the program:
sibling(joe, mary).
sibling(joe, bob).
person(P) :- distinct(sibling(P, _); sibling(_, P)).
Here is the query:
person(P).
I would expect to get 3 names, instead I get 4.
Here is the program:
sibling(joe, mary).
sibling(joe, bob).
person(P) :- distinct(sibling(P, _); sibling(_, P)).
Here is the query:
person(P).
I would expect to get 3 names, instead I get 4.
In short: Prolog keeps track of all variables that come out of the goal, including don't cares (_).
The problem is that you introduce free variables here, that are not distinct. Indeed, as is specified in the documentation of distinct/1 [swi-doc]. The above is functionally equivalent to:
distinct(Goal) :- findall(Goal, Goal, List), list_to_set(List, Set), member(Goal, Set).
Now if we make a simple call with sibling(P, _), we get:
?- Goal = sibling(P, _), distinct(Goal).
Goal = sibling(joe, mary),
P = joe ;
Goal = sibling(joe, bob),
P = joe.
or with a goal with a logical "or":
?- Goal = (sibling(P, _); sibling(_, P)), distinct(Goal).
Goal = (sibling(joe, mary);sibling(_4908, joe)),
P = joe ;
Goal = (sibling(joe, bob);sibling(_4908, joe)),
P = joe ;
Goal = (sibling(mary, _4904);sibling(joe, mary)),
P = mary ;
Goal = (sibling(bob, _4904);sibling(joe, bob)),
P = bob.
So as you can see, Goal is unified twice: once with sibling(joe, mary), and once with sibling(joe, bob). The uniqness filter will not have any effect, since mary is not the same as bob.
We can however define a helper predicate here, to get rid of these variables, like:
person(P) :-
sibling(P, _).
person(P) :-
sibling(_, P).
and then we can query with:
?- Goal = person(P), distinct(Goal).
Goal = person(joe),
P = joe ;
Goal = person(mary),
P = mary ;
Goal = person(bob),
P = bob.
or without using a Goal variable:
?- distinct(person(P)).
P = joe ;
P = mary ;
P = bob.