Control¶
Control predicates manage execution flow — forcing determinism, measuring performance, and controlling search. For delayed execution, see Coroutining.
Determinism¶
once/1¶
once(Goal) — call Goal and commit to the first solution. Prevents
backtracking into the goal.
once is useful when you know a predicate has multiple solutions but you only
want the first:
any_member(ELEM, LIST) <- once(in_(ELEM, LIST))
Test("any") <- (any_member(X, [10, 20, 30]), X == 10)
Timing¶
time_goal/1¶
time_goal(Goal) — execute Goal and print wall-clock and CPU time to stderr.
The goal's solutions pass through unchanged.
Output (to stderr):
time_goal/2¶
time_goal(Goal, Elapsed) — execute Goal and unify Elapsed with the
wall-clock time in seconds (as a float). Useful for programmatic benchmarking.
Patterns & Recipes¶
Benchmark two approaches¶
compare_approaches(GOAL_A, GOAL_B) <- (
time_goal(GOAL_A, TIME_A),
time_goal(GOAL_B, TIME_B),
FASTER == (TIME_A < TIME_B),
writeln(f"A: {TIME_A}s, B: {TIME_B}s")
)
Guard with once¶
Prevent a test predicate from generating multiple successes:
Gotchas¶
oncecuts all choice points — it does not just skip one alternative, it commits fully. Side effects from the first solution will have happened.time_goal/1prints to stderr — not stdout. It won't interfere with write/writeln output.time_goal/2measures wall time — on a loaded system, wall time may be higher than CPU time. Use/1to see both.
See also: If-Then-Else — committed choice with =>,
Tabling — automatic memoization for performance,
Exception Handling — catch/throw for error control flow.