Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Prolog - Custom 'if-then-else'

I'm experimenting with Prolog and tried to make my own "if-then-else" method, so that I don't use the -> ; method, for experimenting's sake. My goal is to make it so that there can be nested ifs and elses in my code if required. Thus far I have got this:

ifthenelse(_, G, G):- G.    %no matter the condition, the goals are the same
ifthenelse(true,G,_):- G.   %if
ifthenelse(false,_,G):- G.  %else

I think my way hardly seems correct. How can I make my own ifthenelse/3 properly?

Thank you

like image 353
Zap Avatar asked Jan 28 '23 09:01

Zap


1 Answers

Preface: What you have implemented is, in an important way, a lot better than the built-in construct you mentioned. I will discuss this point in more detail below.

Regarding the literal question: I think you are quite close already, since you can already nest this to some extent:

?- ifthenelse(C1, ifthenelse(C2,X=1,X=2), X=3).
C1 = C2, C2 = true,
X = 1 ;
C1 = true,
C2 = false,
X = 2 ;
C1 = false,
X = 3.

What now remains is to make it nestable in the condition. For this, you need a way to reify the outcome of conditions, that is, to turn the truth value into a Prolog term that you can reason about symbolically.

See if_/3 for more information: Indexing dif/2

A key property this construct preserves is called logical-purity. In particular, do not erroneously commit to one branch if both are logically possible!


Note on purity: From a declarative point of view, what you have implemented is very nice and has a clear logical interpretation. Taking for example the last two clauses, we can read ifthenelse(C,G1,G2) as: If C is true, then the predicate holds if G1 holds. If C is false, then it holds if G2 holds. Perfectly fine. A semantic "if...then" is not in any way problematic; in fact, every single Horn clause can be read in this way, as an implication from right to left.

In contrast, the built-in construct you mention lacks such a declarative reading in general. For example:

?- ( ( X = 1 ; X = 2 ) -> true ; true ).
X = 1.

But on the other hand:

?- X = 2, ( ( X = 1 ; X = 2 ) -> true ; true ).
X = 2.

So adding a constraint has led to a new solution. A classical logician's nightmare.

Your construct avoids such problems, because it does not prematurely commit to any particular branch. This yields a much more versatile predicate that can also be used in other directions. For example, see that all possible solutions are correctly generated:

?- ifthenelse(C, true, true).
true ;
C = true ;
C = false.

So, I highly encourage your way of formulating this: As you have made perfectly clear, it's your own 'if-then-else', and you are using only pure monotonic constructs to express it.

On a psychological note, I generally prefer to give more room to pure declarative constructs and I added this section only because the comments expressed genuine interest in these aspects.

like image 140
mat Avatar answered Feb 04 '23 21:02

mat