Skip to content

Commit 08b978a

Browse files
committed
Flipped EnvMap as the default.
1 parent 72c2afd commit 08b978a

9 files changed

Lines changed: 44 additions & 38 deletions

File tree

AGENTS.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ deep6/
7171
│ └── utils/
7272
│ └── replaceVars.js # Variable replacement utility
7373
├── tests/ # Test files
74-
│ ├── tests.js # Test dispatcher (~706 tests)
74+
│ ├── tests.js # Test dispatcher (~705 tests)
7575
│ ├── harness.js # Shared test harness
7676
│ ├── test-*.js # Test groups (env, env-map, unify, match, registry, unifiers, traverse, index, edge-cases)
7777
│ └── server.js # Browser test server
@@ -103,8 +103,8 @@ deep6/
103103
- Support open/soft object matching
104104

105105
- **Environment** — two interchangeable implementations of the `EnvLike` method contract:
106-
- `Env` (`src/env.js`) — proto-chain implementation; the default created by `unify()` when no env is passed.
107-
- `EnvMap` (`src/env-map.js`) — `Map`-stack sibling; ~4–8× faster on accumulating-symbol/alias workloads (solver-style use). Opt in by passing `new EnvMap()` to `unify()`.
106+
- `EnvMap` (`src/env-map.js`) — `Map`-stack implementation; the default created by `unify()` when no env is passed. ~4–8× faster on accumulating-symbol/alias workloads (solver-style use).
107+
- `Env` (`src/env.js`) — proto-chain sibling; opt in by passing `new Env()` to `unify()`.
108108
- Behavioural flags (`openObjects`, `circular`, `symbols`, …) live on `env.options` — never on the env instance directly.
109109
- `EnvLike` (`src/env-shared.d.ts`, TS-only) — the method contract both classes implement.
110110
- **Shared primitives** (`src/env-shared.js`) — implementation-agnostic, work with any `EnvLike`:
@@ -232,7 +232,7 @@ clone.registry.push(MyClass, (val, context) => /* clone */);
232232

233233
- **Zero dependencies.** Do not add runtime dependencies.
234234
- All public API is in `src/`.
235-
- Tests verify correctness against ~706 assertions.
235+
- Tests verify correctness against ~705 assertions.
236236
- The project supports modern environments: Node.js, browsers, Deno, Bun.
237237

238238
## When reading the codebase
@@ -248,7 +248,7 @@ clone.registry.push(MyClass, (val, context) => /* clone */);
248248

249249
- `src/unify.js` — Core unification algorithm (non-recursive stack-based)
250250
- `src/env-shared.js` — Implementation-agnostic primitives: Unifier, Variable, any/\_
251-
- `src/env.js`Default proto-chain `Env` implementation
252-
- `src/env-map.js`Map-stack `EnvMap` sibling (perf opt-in for solver workloads)
251+
- `src/env-map.js`Map-stack `EnvMap` implementation (the default)
252+
- `src/env.js`Proto-chain `Env` sibling (opt-in)
253253
- `src/traverse/walk.js` — Foundation for all traversal operations
254254
- `src/index.js` — Main entry point and public API

ARCHITECTURE.md

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,8 @@ The main unification loop (the `unify` function in `src/unify.js`) processes pai
5454

5555
A unification environment carries variable bindings and a stack of generation frames so the solver can backtrack. Two interchangeable implementations conform to the `EnvLike` method contract:
5656

57-
- **`Env`** (`src/env.js`) — the default. Two parallel `null`-prototype objects walked through `Object.create()` chains; one frame per `push()`. Cheap for shallow workloads, pays a dict-mode-deopt tax once enough symbol-named bindings accumulate.
58-
- **`EnvMap`** (`src/env-map.js`) — a sibling implementation. Two parallel `Map`-stacks (one entry per depth, lazily allocated). Identity-hashed lookup avoids the deopt; measures 4–8× faster on accumulating-symbol / alias-binding workloads (typical of solver-style consumers).
57+
- **`EnvMap`** (`src/env-map.js`) — the default. Two parallel `Map`-stacks (one entry per depth, lazily allocated). Identity-hashed lookup avoids dict-mode deopts; measures 4–8× faster on accumulating-symbol / alias-binding workloads (typical of solver-style consumers).
58+
- **`Env`** (`src/env.js`) — a sibling implementation. Two parallel `null`-prototype objects walked through `Object.create()` chains; one frame per `push()`. Cheap for shallow workloads, pays a dict-mode-deopt tax once enough symbol-named bindings accumulate.
5959

6060
Both classes expose the same methods (`push`/`pop`/`revert`/`bindVar`/`bindVal`/`isBound`/`isAlias`/`get`/`getAllValues`) and the same public `depth` and `options` fields. Behavioural flags (`openObjects`, `circular`, `symbols`, etc.) live on `env.options``unify()` merges any per-call options into that bag, so chaining accumulates.
6161

@@ -157,7 +157,7 @@ src/unifiers/ref.js ── src/env.js (Unifier, isVariable)
157157
src/utils/replaceVars.js ── src/env.js (isVariable)
158158
```
159159

160-
Both `src/env.js` and `src/env-map.js` re-export the full shared surface so callers can keep using `import {...} from 'deep6/env.js'` for the proto-chain default, or import directly from `deep6/env-map.js` (which doubles as a single import point covering EnvMap + the shared primitives) for the Map-stack alternative.
160+
Both `src/env.js` and `src/env-map.js` re-export the full shared surface so callers can keep using `import {...} from 'deep6/env.js'` for the proto-chain sibling, or import directly from `deep6/env-map.js` (which doubles as a single import point covering EnvMap + the shared primitives) for the Map-stack default.
161161

162162
## Import paths
163163

@@ -168,13 +168,13 @@ import {equal, clone, match, any} from 'deep6';
168168
// Core unification
169169
import {unify, Variable, variable, Unifier, any, _} from 'deep6/unify.js';
170170

171-
// Environment (proto-chain — default)
171+
// Environment (Map-stack — default)
172+
import {EnvMap} from 'deep6/env-map.js';
173+
174+
// Environment (proto-chain — opt in)
172175
import {Env, Variable, Unifier, any, _} from 'deep6/env.js';
173176
import type {EnvLike} from 'deep6/env.js';
174177

175-
// Environment (Map-stack sibling — opt in)
176-
import {EnvMap} from 'deep6/env-map.js';
177-
178178
// Implementation-agnostic primitives (also re-exported by env.js / env-map.js)
179179
import {Unifier, Variable, variable, isUnifier, isVariable, _, any} from 'deep6/env-shared.js';
180180

CODEBASE.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ src/
8989

9090
## Testing
9191

92-
- **Files:** `tests/test-*.js` (~706 assertions), dispatched by `tests/tests.js`
92+
- **Files:** `tests/test-*.js` (~705 assertions), dispatched by `tests/tests.js`
9393
- **Run:** `npm test`
9494
- **Debug:** `npm run debug` (Node inspector)
9595
- **Browser:** `npm start` (opens test server)

llms-full.txt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -141,12 +141,12 @@ v.get(env); // 2
141141
Two interchangeable environment implementations conform to the `EnvLike` contract; either can be passed to `unify()` as its third argument.
142142

143143
```js
144-
import {Env} from 'deep6/env.js'; // proto-chain (default)
145-
import {EnvMap} from 'deep6/env-map.js'; // Map-stack (4–8× faster on
146-
// accumulating-symbol workloads)
144+
import {EnvMap} from 'deep6/env-map.js'; // Map-stack (default; 4–8× faster
145+
// on accumulating-symbol workloads)
146+
import {Env} from 'deep6/env.js'; // proto-chain (opt-in sibling)
147147
import type {EnvLike} from 'deep6/env.js'; // method contract (TS only)
148148

149-
const env = new Env(); // or new EnvMap();
149+
const env = new EnvMap(); // or new Env();
150150
// public surface (identical across implementations):
151151
// env.depth number, generation counter
152152
// env.options bag of unify() flags

llms.txt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -90,15 +90,15 @@ The third argument can be either an environment instance or an options bag — `
9090

9191
Two interchangeable implementations of the `EnvLike` contract:
9292

93-
- `Env` (`deep6/env.js`) — proto-chain, the default created when no env is passed.
94-
- `EnvMap` (`deep6/env-map.js`) — `Map`-stack sibling; ~4–8× faster on accumulating-symbol/alias workloads (solver-style use). Opt in by passing `new EnvMap()` to `unify()`.
93+
- `EnvMap` (`deep6/env-map.js`) — `Map`-stack, the default created when no env is passed; ~4–8× faster on accumulating-symbol/alias workloads (solver-style use).
94+
- `Env` (`deep6/env.js`) — proto-chain sibling; opt in by passing `new Env()` to `unify()`.
9595

9696
```js
9797
import {unify, variable} from 'deep6/unify.js';
98-
import {EnvMap} from 'deep6/env-map.js';
98+
import {Env} from 'deep6/env.js';
9999

100100
const x = variable('x');
101-
const env = unify({a: x}, {a: 1}, new EnvMap());
101+
const env = unify({a: x}, {a: 1}, new Env());
102102
// x.get(env) === 1
103103
```
104104

src/unify.d.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// Type definitions for deep6 unification
22
// Generated from src/unify.js
33

4-
import {Env} from './env.js';
4+
import type {EnvLike} from './env.js';
55

66
/**
77
* Options for unification
@@ -37,7 +37,9 @@ export interface UnifyOptions {
3737
* Core unification algorithm
3838
*
3939
* Attempts to unify two values, optionally binding variables.
40-
* Returns an Env with bindings on success, or null on failure.
40+
* Returns the environment with bindings on success (the one passed in, or a
41+
* new `EnvMap` — the default implementation — when none was given), or null
42+
* on failure.
4143
*
4244
* `unify.registry` is a flat array of type-specific unifier pairs:
4345
* `[Constructor, handler, Constructor, handler, ...]`. Push pairs to
@@ -54,7 +56,7 @@ export interface UnifyOptions {
5456
* @param options - Unification options (when env is provided separately)
5557
* @returns Environment with bindings, or null on failure
5658
*/
57-
export declare const unify: ((l: unknown, r: unknown, env?: Env | UnifyOptions | null, options?: UnifyOptions) => Env | null) & {
59+
export declare const unify: ((l: unknown, r: unknown, env?: EnvLike | UnifyOptions | null, options?: UnifyOptions) => EnvLike | null) & {
5860
registry: unknown[];
5961
filters: unknown[];
6062
};
@@ -109,8 +111,7 @@ export declare const isSoft: (o: unknown) => boolean;
109111
*/
110112
export declare const isWrapped: (o: unknown) => boolean;
111113

112-
// Re-exports from env.js (value exports, usable as constructors)
113-
export {Env, Unifier, Variable} from './env.js';
114-
export {_, any, isUnifier, isVariable, variable} from './env.js';
114+
// Re-exports from env.js (values usable as constructors, plus the EnvLike contract)
115+
export {Env, Unifier, Variable, _, any, isUnifier, isVariable, variable, type EnvLike} from './env.js';
115116

116117
export default unify;

src/unify.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// @ts-self-types="./unify.d.ts"
22
import {_, Env, Variable, variable, isVariable, Unifier, isUnifier} from './env.js';
3+
import {EnvMap} from './env-map.js';
34

45
// Command
56

@@ -434,7 +435,7 @@ const unify = (l, r, env, options) => {
434435
env = null;
435436
}
436437
if (!env) {
437-
env = new Env();
438+
env = new EnvMap();
438439
}
439440
env.options = options ? {...env.options, ...options} : env.options;
440441
// options: openObjects, openArrays, openMaps, openSets, circular, loose, ignoreFunctions, signedZero, symbols.

tests/test-unify.js

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -61,37 +61,41 @@ export default [
6161
eval(TEST('!unify({a: {a: 1}, b: 2}, {a: {a: 1}, b: 3})'));
6262
},
6363
function test_variables() {
64+
const values = env => {
65+
const all = {};
66+
for (const {name, value} of env.getAllValues()) all[name] = value;
67+
return all;
68+
};
6469
let result = unify(1, 1);
65-
eval(TEST('result && unify(result.values, {})'));
70+
eval(TEST('result && unify(values(result), {})'));
6671
result = unify(1, _);
67-
eval(TEST('result && unify(result.values, {})'));
72+
eval(TEST('result && unify(values(result), {})'));
6873
result = unify(1, v('x'));
6974
eval(TEST('result'));
70-
eval(TEST('unify(result.values, {x: 1})'));
75+
eval(TEST('unify(values(result), {x: 1})'));
7176
eval(TEST("v('x').isBound(result)"));
7277
eval(TEST("v('x').get(result) === 1"));
7378
result = unify(v('y'), v('x'));
7479
eval(TEST('result'));
75-
eval(TEST('unify(result.values, {})'));
76-
eval(TEST('unify(result.variables, {x: {x: 1, y: 1}, y: {x: 1, y: 1}})'));
80+
eval(TEST('unify(values(result), {})'));
7781
eval(TEST("!v('x').isBound(result)"));
7882
eval(TEST("!v('y').isBound(result)"));
7983
eval(TEST("v('x').isAlias('y', result)"));
8084
eval(TEST("v('y').isAlias('x', result)"));
8185
eval(TEST("!v('x').isAlias('z', result)"));
8286
eval(TEST("!v('y').isAlias('z', result)"));
8387
result = unify(v('y'), _);
84-
eval(TEST('result && unify(result.values, {})'));
88+
eval(TEST('result && unify(values(result), {})'));
8589
result = unify([1, v('x')], [v('y'), 2]);
8690
eval(TEST('result'));
87-
eval(TEST('unify(result.values, {x: 2, y: 1})'));
91+
eval(TEST('unify(values(result), {x: 2, y: 1})'));
8892
eval(TEST("v('x').isBound(result)"));
8993
eval(TEST("v('x').get(result) === 2"));
9094
eval(TEST("v('y').isBound(result)"));
9195
eval(TEST("v('y').get(result) === 1"));
9296
result = unify({a: 1, b: v('x')}, {a: v('y'), b: 2});
9397
eval(TEST('result'));
94-
eval(TEST('unify(result.values, {x: 2, y: 1})'));
98+
eval(TEST('unify(values(result), {x: 2, y: 1})'));
9599
eval(TEST("v('x').isBound(result)"));
96100
eval(TEST("v('x').get(result) === 2"));
97101
eval(TEST("v('y').isBound(result)"));

wiki

Submodule wiki updated from 2633d99 to 741f179

0 commit comments

Comments
 (0)