-
-
Notifications
You must be signed in to change notification settings - Fork 0
env: Variable
This is a class that defines instances of logical variables. It is one of the major parts of the unification process and logical solvers.
Variable
is based on Unifier and provides a useful helpers to deal with environments (see Env for more details).
It is defined in env
and can be accessed like that:
import {Variable} from 'deep6/env.js';
Usually a helper factory function is used to create instances of Variable
: variable().
Let's redo the example from Env:
const env = new Env(),
a = new Variable('a'), // can be accessed as 'a'
b = new Variable(), // anonymous
c = new Variable(); // anonymous
a.isBound(env); // false
b.isBound(env); // false
c.isBound(env); // false
// push a generation frame
env.push();
env.bindVal(a.name, 1);
a.isBound(env); // true
b.isBound(env); // false
c.isBound(env); // false
a.get(get); // 1
// push a generation frame
env.push();
a.isBound(env); // true
b.isBound(env); // false
c.isBound(env); // false
env.bindVar(c.name, b.name);
a.isBound(env); // true
b.isBound(env); // false
c.isBound(env); // false
env.isAlias(a, b); // false
env.isAlias(b, c); // true
env.isAlias(c, a); // false
a.isAlias(b, env); // false
b.isAlias(c, env); // true
c.isAlias(a, env); // false
env.bindVal(b.name, 2);
a.isBound(env); // true
b.isBound(env); // true
c.isBound(env); // true
a.get(env); // 1
b.get(env); // 2
c.get(env); // 2
// drop a generation frame
env.pop();
a.isBound(env); // true
b.isBound(env); // false
c.isBound(env); // false
All attributes (bound, aliased) are recorded in an environment, not in a variable itself. It allows using the same variable object with different environments at the same time.
const x = new Variable(),
env1 = new Env(),
env2 = new Env();
x.isBound(env1); // false
x.isBound(env2); // false
env1.bindVal(x.name, 1);
x.isBound(env1); // true
x.isBound(env2); // false
x.get(env1); // 1
env2.bindVal(x.name, 'two');
x.isBound(env1); // true
x.isBound(env2); // true
x.get(env1); // 1
x.get(env2); // 'two'
This class defines objects with the following properties:
-
name
— a string or a symbol that is unique for this logical variable.- It is possible to create several instances of
Variable
with the same name. In this case, logically it will be the same logical variable.
- It is possible to create several instances of
The class defines the following methods:
The constructor creates a logical variable with a given name. It takes the following arguments:
-
name
— an optional string or a symbol value that is unique for this logical variable. If it is not supplied or falsy (an empty string), a unique symbol value is generated.- This value is stored as
name
property so it can be accessed later.
- This value is stored as
It is a convenience method that corresponds to env.isBound(name). The method returns a truthy value if a variable is already bound to a value. Computationally it is very cheap (O(1)
). It takes the following arguments:
-
env
— an environment object (see Env).
The current implementation:
isBound(env) {
return this.name in env.values;
}
It is a convenience method that corresponds to env.isAlias(name1, name2). The method returns a truthy value if variables are already aliased to each other. Computationally it is very cheap (O(1)
). It takes the following arguments:
-
name
— a variable name as a string or a symbol or aVariable
object. -
env
— an environment object (see Env).
The procedure is completely symmetric:
const x = new Variable(),
y = new Variable(),
env = new Env();
env.bindVar(x.name, y.name);
x.isAlias(y, env); // true
y.isAlias(x, env); // true
The current implementation:
isAlias(name, env) {
const u = env.variables[this.name];
return u && u[name instanceof Variable ? name.name : name] === 1;
}
It is a convenience method that corresponds to env.get(name). The method returns a value bound to a variable. Computationally it is very cheap (O(1)
). It takes the following arguments:
-
env
— an environment object (see Env).
No checks are done that the variable is bound. Correctness checks should be done externally before calling the method. If a variable is not bound in the environment, get()
will return undefined
.
Example:
const a = new Variable();
const value = 1; // some arbitrary value
env.bindVal(a.name, value);
a.get(env) === value; // true
The current implementation:
get(env) {
return env.values[this.name];
}
This method is required by Unifier and used internally.
The unification algorithm for a variable:
- If the variable is bound, we unify its value with
val
, by pushing them to thels
-rs
pair. - If
val
is any, it is unified unconditionally. - If
val
is another variable:- If it is bound, we bind the variable to that value.
- Otherwise, we bind these two variables making them aliased.
- Bind the variable to
val
.
In all cases, the actual unification is delegated through the ls
-rs
pair, and the method returns true
.