-
-
Notifications
You must be signed in to change notification settings - Fork 0
unify()
This is the most important algorithm of this package. It implements the first-order unification algorithm using environments and variables.
The unification can be imagined as an advanced version of deep equivalence. It works like that when no variables are used. With variables, it allows ignoring parts of objects or saving them to be retrieved later.
The function can deal with incomplete (open) objects and circular references. See Wrapping for details on open/soft matching modes.
Out of the box it can compare all builtin JavaScript types (numbers, strings, boolean values, symbols, bigints, undefined values), arrays, common objects, and objects of the following builtin classes: Date, RegExp, Int8Array, Uint8Array, Uint8ClampedArray, Int16Array, Uint16Array, Int32Array, Uint32Array, Float32Array, Float64Array, BigInt64Array, BigUint64Array, DataView, ArrayBuffer, Set, Map, URL. This list can be extended for any custom class.
It is defined in unify and can be accessed like that:
import unify from 'deep6/unify.js';The main module provides handy helpers based on unify():
import unify, {open, variable, any} from 'deep6/unify.js';
const x = {a: 1, b: 2, c: ['hi!', 42, null, {}]};
// deep equivalence
!!unify(x, {b: 2, a: 1, c: ['hi!', 42, null, {}]}); // true
// pattern matching
!!unify(x, open({a: 1})); // true
!!unify(x, open({b: 2})); // true
!!unify(x, open({z: 1})); // false
!!unify(x, open({a: 2})); // false
const z = {},
w = {};
z.z = z;
w.z = w;
// circular dependencies
!!unify(z, w, {circular: true}); // true
const v1 = variable(),
v2 = variable(),
v3 = variable();
// variables
const env = unify(x, {a: v1, b: v2, c: v3}); // truth
v1.get(env); // 1
v2.get(env); // 2
v3.get(env); // ['hi!', 42, null, {}]
// anyvar
!!unify(x, {b: any, a: 1, c: any}); // trueArguments:
-
l— a required left value. It can be anything. -
r— a required right value. It can be anything. -
env— an optional Env object with existing variable bindings, or an options object.- When an options object is passed here, it is used as
optionsand no existing environment is used.
- When an options object is passed here, it is used as
-
options— an optional object. The following optional properties are recognized:-
circular— a boolean flag. Whentrue, circular references are detected and handled correctly. Without it, circular structures will cause an infinite loop. -
symbols— a boolean flag. Whentrue, symbol properties are compared. -
loose— a boolean flag. Whentrue, primitives are compared with==rather than===. -
ignoreFunctions— a boolean flag. Whentrue, function values are treated as equivalent. -
signedZero— a boolean flag. Whentrue,+0and-0are treated as different values. -
openObjects— a boolean flag. Whentrue, plain objects are compared in "open" mode (extra properties allowed). -
openArrays— a boolean flag. Whentrue, arrays are compared in "open" mode (extra elements allowed). -
openMaps— a boolean flag. Whentrue, Maps are compared in "open" mode (extra entries allowed). -
openSets— a boolean flag. Whentrue, Sets are compared in "open" mode (extra elements allowed).
-
Returns an Env object with variable bindings on success, or null on failure.
A flat array of Constructor/handler pairs used to unify registered types: [Type1, handler1, Type2, handler2, ...].
Each handler has the signature (l, r, ls, rs, env) => boolean. It should return true on success and false on failure. To delegate deep comparison of sub-values, push them to ls and rs.
Built-in entries handle: Date, RegExp, typed arrays, DataView, ArrayBuffer, Set, Map, URL.
Plain objects (prototype Object.prototype or null) and arrays are fast-pathed and skip the registry. To match plain objects by a custom criterion, use unify.filters instead.
Extend with:
unify.registry.push(MyClass, (l, r, ls, rs, env) => {
if (!(l instanceof MyClass) || !(r instanceof MyClass)) return false;
ls.push(l.value);
rs.push(r.value);
return true;
});A flat array of predicate/handler pairs: [test1, handler1, test2, handler2, ...].
Each test has the signature (l, r) => boolean and determines whether the handler should run. Each handler has the signature (l, r, ls, rs, env) => boolean.
Filters are checked after the registry. They are useful when matching is not based on instanceof but on some other property.
unify.filters.push(
(l, r) => l.flag || r.flag, // test
(l, r, ls, rs, env) => {
// handler
ls.push(l.name);
rs.push(r.name);
return true;
}
);