Control Flow Statements

Flora has a number of “control flow statements” which may be partially familiar from programming languages. For examples,

  • \if \then
  • \if \then \else
  • \unless \do
  • \while \do
  • \while \until
  • \while \loop
  • \loop \until

The Flora manual has full details about them. We have mostly found the first three types to be useful. This is especially true because Flora does not have a dependable rule ordering (see the discussion in Recursive rules). The if-then-else statement provides a way to achieve the same effect as ordering-and-cuts that is common in plain prolog, at the cost of putting all the logic in one big rule, which may be difficult to read. [1]

As an example, consider the task of finding all the male individuals from a list of people. Assuming we have a male predicate, we could use the following rules in XSB (Note that this does not work in Flora):

males([],[]).
males([P|Ps],[P|Pms]) :- male(P), !, males(Ps,Pms).
males([P|Ps],Pms) :- males(Ps,Pms).

These rules assume that the rules are executed in the order shown. The cut in the second rule prevents XSB from trying the third rule, in the case where the second rule applies (i.e., the first member of the list is male). Essentially, the third rule is the “else” case. But in Flora, the rules can execute in any order, possibly firing the third rule before trying the second rule, and therefore skipping some or all of the males in the list.

However we could make it work in Flora by introducing a negation. For example:

males([],[]).
males([?P|?Ps],[?P|?Pms]) :- male(?P), males(?Ps,?Pms).
males([?P|?Ps],?Pms) :- \+ male(?P), males(?Ps,?Pms).

But this is verbose, and causes redundant computation of the male(?P) check. It is especially unfortunate if the condition to check is a larger formula that has to be repeated (unlike this simple example).

Instead, using if-then-else we can achieve the same result by using one Flora rule:

males(?Ps,?Ms) :-
  \if ?Ps = [] \then ?Ms = []
  \else \if ?Ps = [?P|?Pss] \then
    (males(?Pss,?Mss),
     \if male(?P) \then ?Ms = [?P|?Mss]
     \else ?Ms = ?Mss).

Here, we are using two nested if-then-else statements: One to check if the list is empty or not, and a second to check whether the first member is male.

Note the parentheses around the statement:

(males(?Pss,?Mss),
 \if male(?P) \then ?Ms = [?P|?Mss]
 \else ?Ms = ?Mss).

They are needed in order to make this entire composite statement the else-if part of the top-level if-then-else statement (see Precedence and Parentheses). You can think of these parentheses as analogous to the curly brackets used with if-else statements in C or Java when you have more than one statement in a condition. We also recommend using indentation to show the correct nesting of if-then-elses.

As an alternative to having one big nested rule, we can do the first check by relying on unification in the rule heads, as in the Prolog version of the rules, and only do the second check using if-then-else:

males([],[]).
males([?P|?Ps],?Ms) :-
  males(?Ps,?Mss),
  \if male(?P) \then ?Ms = [?P|?Mss]
  \else ?Ms = ?Mss).

This works because no term can unify with both rule heads, so there is no concern with incorrect rule ordering.

This is still more verbose than the Prolog version, but it gives a more declarative specification that is independent of non-logical details like rule ordering.

[1]Combining multiple rules into one could also be seen as an advantage