0

I would like to implement a simple if and only if relation in my Prolog code. Here is a simple example of what I would like to have:

?- a_iff_b(a, b).
true.

?- a_iff_b(x, x).
true.

?- a_iff_b(a, x).
false.

?- a_iff_b(x, b).
false.

You get the point—the predicate should be true if either the first argument is a and the second argument is b or if neither is the first argument a nor is the second b. I hope that makes sense. Mathematically speaking, X = a <==> Y = b.

Here is my attempt:

a_iff_b(X, Y) :-
        X = a, Y = b;
        X \= a, Y \= b.

But this code introduces a choice point:

?- a_iff_b(a, b).
true ;
false.

Why? It seems that the following works, I mean it does not introduce the choice point:

a_iff_b(X, Y) :- (X = a -> Y = b; Y \= b).

However, I find this a little less readable, especially because in my actual predicate, the two sides of the equivalence are more complex (the are also predicates).

bp99
  • 338
  • 3
  • 15

1 Answers1

3

Your code introduces a choice point because it contains a choice introduced by the ; operator. You might think of this as a "logical or" operator, and somewhat informally it is. But procedurally it is an "introduce a choice point here, and on backtracking, explore the second branch as well" operator.

It's true that a Sufficiently Powerful Prolog Compiler™ might be able to recognize that the two branches here are mutually exclusive, but it appears that your Prolog system isn't that powerful. I would be at least mildly surprised if any Prolog you can get for free would be able to do this without a choice point. Especially if your actual conditions are more complex, as you say.

If you want this to be more readable, some tips:

  • In general, never use ; at the end of a line as if it were just a variant of ,. It's too easy to miss it, since the general expectation is that lines end in ,. Try to format your code in some way that really makes the ; stick out (see examples below).

  • In general, if you want to use ; (not _ -> _ ; _), especially as the single top-level goal in a clause, consider using separate clauses instead:

      a_iff_b(a, b).
      a_iff_b(X, Y) :-
          X \= a,
          Y \= b.
    

In any case, all of the following are possible ways of cutting away the choice:

a_iff_b_1(a, b) :-
    !.
a_iff_b_1(X, Y) :-
    X \= a,
    Y \= b.

a_iff_b_2(X, Y) :-
    (   X = a, Y = b
    ->  true
    ;   X \= a, Y \= b ).

a_iff_b_3(X, Y) :-
    (   X = a, Y = b,
        !
    ;   X \= a, Y \= b ).

a_iff_b_4(X, Y) :-
    (   X = a, Y = b
    ;   X \= a, Y \= b ),
    !.

a_iff_b_5(X, Y) :-
    once(( X = a, Y = b
         ; X \= a, Y \= b )).
Isabelle Newbie
  • 9,258
  • 1
  • 20
  • 32
  • Thank you, this clears up a lot of things. I knew I could use a cut, but I got the notion while looking around on SO that cuts should be avoided as much as possible—I suppose this is one of the cases where it makes perfect sense, since it eliminates a pointless choice point. Regarding the style of `;` placement: I have seen this strange (to me) formatting, but never understood why it’s better this way. Thanks for explaining it. I think at the end of the day the `->` construct is the most readable for me. I was just hoping there is an elegant solution to this I was missing :-) – bp99 Oct 30 '20 at 17:49
  • I'm glad this helped. Cuts are indeed something that should be used carefully and sparingly. Personally I prefer the `->` variant because its effects are more local. Especially with parentheses around the whole construct you see the limited scope very well. – Isabelle Newbie Oct 30 '20 at 19:54
  • Good point. Concerning the style, would you consider it fine to have the entire `(_ -> _; _)` statement on one line? I like to keep lines short, but the ‘terms’ (?) in this case aren’t long. – bp99 Oct 30 '20 at 20:54
  • 1
    I think having such constructs on one short, simple line is a valid style. It's not my preference, but I don't think it's bad style the way I think that "hiding" `;` is bad style. – Isabelle Newbie Oct 30 '20 at 22:38