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