ISO Prolog Unification: Transitioning to Occurs Check Validation
0 reputation · 01 Jun 2022, 19:10 UTC
0 reputation · 01 Jun 2022, 19:10 UTC
Standard ISO Prolog implementations typically omit the occurs check during unification to optimize performance. This behavior allows for the creation of cyclic terms, which can lead to unsoundness or infinite recursion in certain predicates.
When transitioning a codebase from a permissive environment to one requiring strict logical soundness, the mechanism for enforcing the occurs check must be explicitly managed. The challenge lies in balancing the performance penalty of the check against the risk of cyclic terms in complex data structures.
Which predicates or configuration flags are recommended to enforce the occurs check globally? How does the behavior of cyclic term detection differ between standard unification and the unify_with_occurs_check/2 predicate?
35275 reputation · 01 Jun 2022, 22:55 UTC
To enforce the occurs check globally in an ISO-compliant Prolog environment, you should use the flag/2 predicate to set the occurs_check option. While the specific flag name can vary slightly by implementation (e.g., SWI-Prolog uses occurs_check), the general mechanism is as follows:
flag(occurs_check, true).
Once this flag is set to true, the standard unification operator =/2 will perform the occurs check, ensuring that a variable cannot be unified with a term that contains it, thereby preventing the creation of cyclic terms.
The primary difference between standard unification (without the check) and unify_with_occurs_check/2 lies in how they handle recursive definitions:
| Feature | Standard Unification (=/2) |
unify_with_occurs_check/2 |
|---|---|---|
| Cyclic Terms | Permits cyclic terms (Rational Trees). | Fails if a cyclic term would be created. |
| Complexity | Generally linear time. | Proportional to the size of the terms. |
| Soundness | Unsound in first-order logic. | Logically sound. |
In a permissive environment, X = f(X) succeeds, creating a pointer to the variable itself. This can lead to infinite recursion during printing or traversal. Conversely, unify_with_occurs_check(X, f(X)) will immediately return false, as it detects that X occurs within the term f(X).
=/2 calls with unify_with_occurs_check/2 rather than toggling the global flag.% Should fail if occurs check is active
?- X = f(X).
% Should always fail
?- unify_with_occurs_check(X, f(X)).
Diagnostic Note: Please specify which Prolog implementation (e.g., SWI, SICStus, GNU Prolog) you are using, as the exact flag naming and availability of global toggles vary across vendors.
Use comments to ask for clarification. Post a solution as an answer.
35,275 reputation · 01 Jun 2022, 22:15 UTC
ISO‑Prolog mandates an occurs check during unification, but most engines (e.g., SWI‑Prolog, ECLiPSe) turn it off by default for speed. The correct way to enable it globally is:
set_prolog_flag(occurs_check, true).
This predicate is part of the ISO standard; flag/2 is an engine‑specific alias that may not be available on all systems. Once the flag is set, the ordinary =/2 (and unify/2) behave exactly like unify_with_occurs_check/2, so the dedicated predicate becomes redundant. However, using unify_with_occurs_check/2 can still be useful for code that wants to enforce the check only in a narrow scope without touching the global flag.