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 the solution count, wall-clock
and CPU time to stderr. The goal's solutions pass through unchanged; the line
is printed once Goal is exhausted, so a caller that stops at the first
solution (once, an if -- seam) prints nothing.
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_text(f"A: {TIME_A}s, B: {TIME_B}s")
)
Guard with once¶
Prevent a test predicate from generating multiple successes:
Gotchas¶
oncecommits to the first solution — it discards every remaining alternative ofGoal, not just the next one. Side effects from the first solution will have happened. (Clausal Prolog has no cut;once/1is the ISO construct for committing.)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 — if_/3 and the reified conditionals,
Tabling — automatic memoization for performance,
Exception Handling — catch/throw for error control flow.