Why no if-but-not-else construct?

I often find myself using the (P -> Q; true) formulation, when I need to do an extra check or take an extra action but only when a certain precondition is true; when it isn’t, the clause can proceed as it would otherwise. Why isn’t there a built-in operator or predicate that does this? Perhaps something that binds tighter than ,/2, like say:

:- op(800, xfy, ?).

% Whenever P succeeds, then Q must succeed, but \+P is fine
P ? Q :- P *-> Q; true.

% Match *or* generate "(At)" in text, when At is an atom
parenthesized_atom(At) -->
    {ground(At) ? atom_codes(At,AC)},

Note that this isn’t a “how can I do this, please” - I have a number of ways I can implement this, and it’s not all that cumbersome (and more to the point, I want to make sure that I am writing idiomatic Prolog). It’s more of a “why was Prolog designed this way?” combined with some amount of “are there hidden pitfalls to doing this kind of thing that I’m not aware of?”

I don’t know the reason (I have a couple of theories), but I do know that Logtalk’s linter complains about (P->Q) but doesn’t complain about (P->Q;fail).

Paulo Moura (author of Logtalk) doesn’t seem to be in this discussion group, but maybe somebody knows the reason?

I think the reason is that from a logical point of view, P -> Q is seen as (in logic) P implies Q.

Now, when P implies Q (in logic), you are not saying anything at all about what happens if not P (you could still have Q even if not P), and therefore I think it would be sensible from this point of view to always specify what happens if not P when you are implementing a programming language.

Almost every time I use (P->Q;true), Q is something “impure”, such as a print statement – I have more examples of (P->true;Q).

So, maybe it’s a Good Thing that there’s no short-cut for (P->Q;true) because it’s a “code smell”.

Yes, I don’t often need this construct.

On the other hand (P->true;Q) is much more common because P unifies what needs to be unified.

(bad_input(X)->throw(error(bad_input(X), _));true) … this is similar to must_be/2 or domain_error/2.
Although there’s been a long discussion about failure vs throwing an exception.

Another useful case: In a DCG: ({some_test(X)}->[X];[]).

Ooh fun, I’ll play! Let’s see. I’ll mark the trues that get executed with %%%%

?- ((true->true),true;true).
%    %%%%  %%%%  %%%%^ Choice point here, this isn't a ->; construct
?- ((true->true);true).
%    %%%%  %%%%        No choice points, the -> cuts the ; choice
?- ((true->true);true;(true->true)).
%    %%%%  %%%%      ^ Choice point here, the second ; doesn't get cut
?- (((true->true);true);(true->true)).
%     %%%%  %%%%       ^ Choice point here, syntactically equivalent to the above

I think these are correct. Not so sure about #3, but I’m pretty sure the precedence rules group on the left ;/2, so only that one gets cut by the ->/2.

Well? How’d I do? :grinning_face_with_smiling_eyes:

Ah! I had my precedence rules for ;/2 backward. I always have trouble with those… which is why write_canonical/1 is my best friend :grinning_face_with_smiling_eyes: