Jacek
Jacek

Reputation: 1088

Optional binding in Prolog

Let's imagine a simple database of genealogy facts where mother(M, C) and father(F, C) denotes that M/F is the mother/father of child C.

I've written a rule to find known parents of a child (zero, one or both):

parents(C, M, F) :-
  (mother(M, C) -> true; true),
  (father(M, C) -> true; true).

which binds M and F if they are known and leaves them unbound otherwise.

It works fine, which means that for a set of facts:

mother(m1, c1).
father(f1, c1).
mother(m2, c2).

a call to parents(c1, M, F) returns:

M = m1,
F = f1.

while parents(c2, M, F) returns:

M = m2.

but the use of the arrow operators seems a little strange to me. Am I missing something basic? Can the (X -> true ; true) calls be avoided/simplified?

Any help appreciated.

Cheers,

Upvotes: 3

Views: 170

Answers (2)

mat
mat

Reputation: 40768

From a logical perspective, a major mistake in this program is its incompleteness.

Consider for example the most general query:

?- parents(X, Y, C).
X = c1,
Y = m1.

So, no solution for c2 is reported.

But such a solution exists, as can be seen with:

?- parents(c2, Y, C).
Y = m2.

So, which is it, is there a solution or not?

Such mistakes almost invariably arise if you use (->)/2 and other constructs that violate logical purity of your code. Please see for more information.

Hence, from a logical perspective, I can only recommend to avoid such constructs, since they defeat the primary advantage of a logic programming language to begin with: The ability to reason logically about your programs.

Instead, focus on a clear description of the relations you want to describe, and state the conditions that make them true. This will allow you to use your Prolog programs in a sensible way.

EDIT: I see you prefer a botched program. For this purpose, I recommend ignore/1. ignore(Goal) calls Goal as once(Goal), and succeeds. You can use this to simplify your program and still ensure that it remains incomplete.

Upvotes: 4

Will Ness
Will Ness

Reputation: 71065

Prolog is a real down to earth programming language. It has a pure subset. Both have their place.

once( (A ; true) ) is the answer to the question "how can we simplify (A -> true; true)".

If you want more purity, you could write (A *-> true ; true ) with the "soft cut" *-> which admits all solutions from the successful A and only switches to the unsuccessful branch in case A didn't produce any. See also e.g. this answer of mine for more discussion.

Another variant is (A ; \+ A).

Upvotes: 1

Related Questions