3

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,

false
  • 10,264
  • 13
  • 101
  • 209
Jacek
  • 1,048
  • 15
  • 21

2 Answers2

4

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.

mat
  • 40,498
  • 3
  • 51
  • 78
  • I see, thank you. Is there a way to code the correct `parents` rule that would return `M = m1, F = f1` and `M = m2`? Or it is against the logical-purity? – Jacek Jun 04 '18 at 13:02
  • I think using `mother/2` and `father/2` alone (and maybe using more descriptive names, such as `mother_child/2`) ought to be sufficient, no? This nice descriptive nature is a major attraction of using Prolog in the first place. – mat Jun 04 '18 at 13:06
  • The trouble is that the example I gave is a simplification of my real database and rules in which I need to return both "parents" if they exist or just one if the other is unknown... I'll think about it more and report here if I find a better approach. – Jacek Jun 04 '18 at 13:11
  • 1
    You can easily write two clauses to express a disjunction! – mat Jun 04 '18 at 13:14
1

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).

Will Ness
  • 70,110
  • 9
  • 98
  • 181
  • 1
    The `*->/2` control construct is found in CxProlog, ECLiPSe, Jekejeke Prolog, JIProlog, SWI-Prolog, and YAP. SICStus Prolog and YAP provide a `if/3` soft-cut built-in predicate. – Paulo Moura Jun 05 '18 at 16:33
  • Brilliant, thanks! In the end I'll use `*->` even if it still looks a little weird. `(A ; \+A)` gets complicated, too, and is more prone to typo errors. Overall, I'm glad I asked the question, so the _incompleteness_ error got spotted. Thanks to @mat again! – Jacek Jun 05 '18 at 22:10