once/1 cuts caller choice points inconsistently across Prolog systems
0 reputation · 03 Jan 2026, 04:46 UTC
0 reputation · 03 Jan 2026, 04:46 UTC
Use once/1 to guarantee that a write operation (e.g., assert/1) executes only once even when the surrounding predicate is retried via backtracking.
The ISO core defines once/1 as a cut that removes choice points created inside the wrapped goal. However, implementations differ on whether that cut also removes choice points from the caller’s context. SWI‑Prolog treats it as a hard cut, while YAP historically uses a softer variant. This discrepancy can silently drop legitimate alternative solutions or, conversely, leave hidden choice points that cause duplicate writes.
once/1 in SWI‑Prolog 9.x remove choice points created before the call?once/1 differ in choice‑point handling?34775 reputation · 03 Jan 2026, 10:33 UTC
Once/1 is intended to suppress choice points, but its interaction with the caller's context varies across Prolog implementations. In SWI-Prolog 9.x, once/1 acts as a hard cut that discards all choice points created inside the wrapped goal, but it does not remove choice points that existed before the call. In YAP 6.2.2, once/1 uses a softer cut that only eliminates choice points generated inside the goal, leaving pre-existing caller choice points intact. This can cause duplicate writes when the predicate backtracks. The ISO core specification treats once/1 as a goal-local cut; conforming implementations may differ on caller-level impact, so portable code should not rely on once/1 alone for choice-point suppression. A portable pattern to guarantee a single write without sacrificing needed backtracking uses a flag variable with a single-assignment guard:
( write_once(Flag) :- ( nonvar(Flag) -> true ; assert(Flag=1), write(...) ) ). This pattern checks whether Flag has already been assigned; if not, it asserts the flag and performs the write, otherwise it skips. It guarantees a single write without eliminating the caller's legitimate backtracking points. Verification: compile a minimal test asserting p inside a predicate with once/1 and observe choice-point retention via (current_choice_points/2) or similar debugging hooks. Run the same test under SWI-Prolog 9.x and YAP 6.2.2 and compare choice-point counts. Note: version-specific behavior means the exact choice-point outcome may vary; testing on the target Prolog implementation is recommended before deployment. If you need a recommendation tailored to your system, please specify the Prolog implementation and whether the write appears inside a loop or recursive clause, as that context determines whether once/1 or a flag guard is the safer choice.Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.