Reuse logic variables with Scope¶
How to build terms that share the same logic variable in multiple places, using the core module's Scope
API — for example to construct the Prolog rule member(H, [_|T]) :- member(H, T)., where H and T each
occur twice and must refer to the same variable both times.
This is a usage recipe; see the Explanation section for why Var equality is scoped the way it is.
1. Know why you need this¶
Two Var instances created independently are never equal, even with the same name:
Var.of("X") == Var.of("X") // always false
To reuse a variable, you must keep a reference to the same Var instance and reuse that reference. Doing
this by hand with plain Kotlin vals works but gets cumbersome for anything beyond a couple of variables.
2. Create a Scope and build terms through it¶
Scope is a Term factory that remembers the variables it has already created, by name, and hands back the
same instance on a repeated request within that scope:
/**
* A small, stateful factory of [Term]s that remembers the [Var]iables it has already created, by [Var.name].
*
Use Scope.empty { ... } to build one or more terms inside a fresh scope, calling its factory methods
(varOf, atomOf, structOf, consOf, ruleOf, ...) instead of the static Var.of/Struct.of/etc.
equivalents:
import it.unibo.tuprolog.core.Scope
val memberRule = Scope.empty {
ruleOf(
structOf("member", varOf("H"), consOf(anonymous(), varOf("T"))),
structOf("member", varOf("H"), varOf("T")),
)
}
Every varOf("H") call within the same scope returns the same Var instance, so the two occurrences of
H (and of T) in the rule above are genuinely the same variable — unlike two independently-created
Var.of("H") instances.
3. Use Scope.of to seed a scope with pre-existing variables¶
If you already have some Vars (e.g. from a previously parsed term) and want further term-building to reuse
them by name, seed the scope with them instead of starting empty:
import it.unibo.tuprolog.core.Scope
import it.unibo.tuprolog.core.Var
val x = Var.of("X")
val scope = Scope.of(x)
scope.varOf("X") === x // true: same instance, retrieved by name
4. Inspect the variables collected by a scope¶
Every Scope exposes the variables it knows about as a Map<String, Var>:
Scope.empty {
varOf("X")
varOf("Y")
variables // mapOf("X" to <Var X>, "Y" to <Var Y>)
}
Notes¶
anonymous()(and its aliaswhatever()) always creates a fresh anonymous variable, even within the same scope — unlike named variables, these are never reused. If you specifically want the same "don't care" variable reused across a scope, callvarOf("_")instead: since it goes through the same by-name cache as any other named variable, repeated calls return the same instance.Scopes are mutable and meant to be short-lived: create a new one per logical unit of work (e.g. one per clause when building/parsing a theory), rather than reusing a singleScopeacross unrelated terms.Term.freshCopy()uses exactly this mechanism internally, creating a freshScopeto consistently rename all variables in a term.