Control and cut

! is a hard cut: it discards not only a predicate’s untried rules but the choice points of premises already solved in the same rule (Logic Programming, Formal semantics). The rest are the control constructs built on it.

Cut and disjunction

! discards every choice point created since the rule it appears in was selected, including one opened by a ; earlier in the same body, not only the rule’s own untried alternatives:

describe N R :- 0 is N mod 2, !, R = "even".
describe _ "odd".

Once the first rule’s guard succeeds, ! commits: on backtracking, Elpi does not try to make R something other than "even", and does not fall through to the second rule either. Both were choices made after the cut’s rule was entered.

not, if and if2

not G succeeds iff G has no solution. It is defined as not X :- X, !, fail. not _., so it commits to the first solution of G, if any, before failing. if C T E commits to the first solution of C, if any, and runs T; otherwise it runs E. It is a packaged cut, cheaper to read than (C, !, T ; E). if2 is the same with two conditions tried in order, and a final else:

func if  (pred), (func), (func).
func if2 (pred), (func), (pred), (func), (func).

halt and stop

halt (variadic: it accepts anything print-able) stops the whole process immediately, printing its arguments first, for a fatal error. stop fails the current goal without terminating the process, so outer alternatives are still tried.

Taming backtracking

std.once G is G with an implicit cut right after its first success, for a relation that naturally has several solutions when only the first is wanted. std.do! [G1, G2, …] runs a sequence of goals each followed by a cut, so a failure never triggers backtracking into an earlier one, closer to imperative sequencing than a plain conjunction.

Each of these in one program, a cut committing a disjunction, then not, if, std.once and std.do!:

../code/control.elpi:

 1% Cut committing a disjunction (describe), not, if, std.once, std.do!.
 2
 3pred describe int -> string.
 4describe N R :- 0 is N mod 2, !, R = "even".
 5describe _ "odd".
 6
 7pred even int.
 8even N :- 0 is N mod 2.
 9
10main :-
11  describe 4 R1, print "4 is" R1,
12  describe 3 R2, print "3 is" R2,
13  not (even 3), print "not (even 3) succeeds",
14  if (even 4) (print "if: 4 is even") (print "if: 4 is odd"),
15  std.once (std.mem [3,4,5] X), print "once picked the first match:" X,
16  std.do! [ print "do!: step 1", print "do!: step 2" ].
4 is even
3 is odd
not (even 3) succeeds
if: 4 is even
once picked the first match: 3
do!: step 1
do!: step 2