Attributed-Variable Combinators (freeze, dif)


Overview

std.freeze exposes two high-level combinators built on Eta’s attributed-variable substrate:

FormPurpose
(freeze v goal-thunk)Suspend goal-thunk until v becomes bound
(dif x y)Structural disequality — succeeds iff x and y cannot become equal without further bindings

Both are pure stdlib: they use the public attribute primitives (put-attr, get-attr, register-attr-hook!, attr-var?) and the unified VM trail. No new opcodes.

(import std.freeze)

Attribute Primitives Recap

FormDescription
(put-attr v 'mod val)Attach attribute mod with value val to logic-var v (trailed)
(get-attr v 'mod)Read attribute mod, or #f
(del-attr v 'mod)Remove attribute mod (trailed)
(attr-var? v)#t iff v has at least one attribute
(register-attr-hook! 'mod hook)Register (hook var bound-val attr-val) — fires when var is bound; returning #f fails the unification and triggers rollback

Hooks fire inside unification. A hook that needs to post further constraints typically returns #t and queues work on the 'clp.prop async queue (see CLP(FD)).


freeze

(freeze v goal-thunk) -> #t
(define x (logic-var))
(freeze x (lambda () (println (list 'x 'is (deref-lvar x))) #t))
;; ... later ...
(unify x 42)   ; prints "x is 42" as a side effect of unify

dif

(dif x y) -> #t | #f

Maintains, per participating variable, a list of witness terms that must never become structurally equal. On every binding the 'dif hook re-probes each pending witness:

Probing uses a trail-mark / unify / unwind-trail sandbox so it never leaves bindings behind.

(define x (logic-var))
(define y (logic-var))
(dif x y)
(unify x 1) (unify y 1)  ; => #f, dif rolled back

dif composes with findall / run* / run-n from std.logic: on backtrack the trail restores witnesses exactly.


Composition

Both combinators share the same trail-and-attribute discipline as CLP(R) and CLP(FD), so they freely combine:

(import std.clp)
(import std.freeze)

(define x (logic-var))
(clp:domain x 0 9)
(freeze x (lambda () (println (list 'bound (deref-lvar x))) #t))
(clp:labeling (list x))
;; freeze hook fires once per labelled choice, with full backtrack support.

Source Locations

ComponentFile
std.freeze modulestdlib/std/freeze.eta
Attribute substrateeta/core/src/eta/runtime/vm/vm.cpp
Testsstdlib/tests/attrvar_freeze_dif_trail.test.eta