mcard-js 2.1.1 → 2.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/README.md +82 -2
  2. package/dist/index.d.ts +2 -0
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.js +3 -0
  5. package/dist/index.js.map +1 -1
  6. package/dist/ptr/lambda/AlphaConversion.d.ts +42 -0
  7. package/dist/ptr/lambda/AlphaConversion.d.ts.map +1 -0
  8. package/dist/ptr/lambda/AlphaConversion.js +244 -0
  9. package/dist/ptr/lambda/AlphaConversion.js.map +1 -0
  10. package/dist/ptr/lambda/BetaReduction.d.ts +73 -0
  11. package/dist/ptr/lambda/BetaReduction.d.ts.map +1 -0
  12. package/dist/ptr/lambda/BetaReduction.js +322 -0
  13. package/dist/ptr/lambda/BetaReduction.js.map +1 -0
  14. package/dist/ptr/lambda/EtaConversion.d.ts +65 -0
  15. package/dist/ptr/lambda/EtaConversion.d.ts.map +1 -0
  16. package/dist/ptr/lambda/EtaConversion.js +228 -0
  17. package/dist/ptr/lambda/EtaConversion.js.map +1 -0
  18. package/dist/ptr/lambda/FreeVariables.d.ts +44 -0
  19. package/dist/ptr/lambda/FreeVariables.d.ts.map +1 -0
  20. package/dist/ptr/lambda/FreeVariables.js +207 -0
  21. package/dist/ptr/lambda/FreeVariables.js.map +1 -0
  22. package/dist/ptr/lambda/LambdaRuntime.d.ts +80 -0
  23. package/dist/ptr/lambda/LambdaRuntime.d.ts.map +1 -0
  24. package/dist/ptr/lambda/LambdaRuntime.js +417 -0
  25. package/dist/ptr/lambda/LambdaRuntime.js.map +1 -0
  26. package/dist/ptr/lambda/LambdaTerm.d.ts +95 -0
  27. package/dist/ptr/lambda/LambdaTerm.d.ts.map +1 -0
  28. package/dist/ptr/lambda/LambdaTerm.js +159 -0
  29. package/dist/ptr/lambda/LambdaTerm.js.map +1 -0
  30. package/dist/ptr/lambda/index.d.ts +24 -0
  31. package/dist/ptr/lambda/index.d.ts.map +1 -0
  32. package/dist/ptr/lambda/index.js +34 -0
  33. package/dist/ptr/lambda/index.js.map +1 -0
  34. package/dist/ptr/node/CLMRunner.d.ts +30 -0
  35. package/dist/ptr/node/CLMRunner.d.ts.map +1 -1
  36. package/dist/ptr/node/CLMRunner.js +167 -0
  37. package/dist/ptr/node/CLMRunner.js.map +1 -1
  38. package/dist/ptr/node/Runtimes.d.ts +8 -1
  39. package/dist/ptr/node/Runtimes.d.ts.map +1 -1
  40. package/dist/ptr/node/Runtimes.js +8 -1
  41. package/dist/ptr/node/Runtimes.js.map +1 -1
  42. package/dist/storage/schema.d.ts.map +1 -1
  43. package/dist/storage/schema.js +5 -1
  44. package/dist/storage/schema.js.map +1 -1
  45. package/package.json +6 -2
@@ -0,0 +1,322 @@
1
+ /**
2
+ * Beta Reduction (β-reduction)
3
+ *
4
+ * The computational heart of Lambda Calculus - function application.
5
+ *
6
+ * Rule: (λx.M) N →β M[x:=N]
7
+ *
8
+ * A term of the form (λx.M) N is called a "beta-redex" (reducible expression).
9
+ * Beta reduction substitutes the argument N for all free occurrences of x in M.
10
+ *
11
+ * IMPORTANT: Must handle capture avoidance - if N contains free variables
12
+ * that would become bound in M, we must α-rename first.
13
+ *
14
+ * @module mcard-js/ptr/lambda/BetaReduction
15
+ */
16
+ import { loadTerm, storeTerm, mkAbs, mkApp, isAbs, isApp } from './LambdaTerm';
17
+ import { freeVariables, generateFresh } from './FreeVariables';
18
+ import { alphaRename } from './AlphaConversion';
19
+ import { IO } from '../../monads/IO';
20
+ import { Either } from '../../monads/Either';
21
+ import { Maybe } from '../../monads/Maybe';
22
+ // ─────────────────────────────────────────────────────────────────────────────
23
+ // Beta Redex Detection
24
+ // ─────────────────────────────────────────────────────────────────────────────
25
+ /**
26
+ * Check if a term is a beta-redex: (λx.M) N
27
+ */
28
+ export function isRedex(collection, termHash) {
29
+ return IO.of(async () => {
30
+ const term = await loadTerm(collection, termHash);
31
+ if (!term || !isApp(term))
32
+ return false;
33
+ const func = await loadTerm(collection, term.func);
34
+ return func !== null && isAbs(func);
35
+ });
36
+ }
37
+ /**
38
+ * Find the leftmost-outermost redex (normal order reduction)
39
+ * Returns Maybe<hash of redex>
40
+ */
41
+ export function findLeftmostRedex(collection, termHash) {
42
+ return IO.of(async () => {
43
+ return findRedexNormalOrder(collection, termHash);
44
+ });
45
+ }
46
+ async function findRedexNormalOrder(collection, termHash) {
47
+ const term = await loadTerm(collection, termHash);
48
+ if (!term)
49
+ return Maybe.nothing();
50
+ switch (term.tag) {
51
+ case 'Var':
52
+ // Variables are not redexes
53
+ return Maybe.nothing();
54
+ case 'Abs':
55
+ // Look inside the body
56
+ return findRedexNormalOrder(collection, term.body);
57
+ case 'App': {
58
+ // Check if this application is a redex
59
+ const func = await loadTerm(collection, term.func);
60
+ if (func && isAbs(func)) {
61
+ // This is a redex!
62
+ return Maybe.just(termHash);
63
+ }
64
+ // Not a redex - search in func first (leftmost), then arg
65
+ const funcRedex = await findRedexNormalOrder(collection, term.func);
66
+ if (funcRedex.isJust)
67
+ return funcRedex;
68
+ return findRedexNormalOrder(collection, term.arg);
69
+ }
70
+ }
71
+ }
72
+ /**
73
+ * Find the leftmost-innermost redex (applicative order reduction)
74
+ */
75
+ export function findInnermostRedex(collection, termHash) {
76
+ return IO.of(async () => {
77
+ return findRedexApplicativeOrder(collection, termHash);
78
+ });
79
+ }
80
+ async function findRedexApplicativeOrder(collection, termHash) {
81
+ const term = await loadTerm(collection, termHash);
82
+ if (!term)
83
+ return Maybe.nothing();
84
+ switch (term.tag) {
85
+ case 'Var':
86
+ return Maybe.nothing();
87
+ case 'Abs':
88
+ return findRedexApplicativeOrder(collection, term.body);
89
+ case 'App': {
90
+ // Search in subterms first (innermost)
91
+ const funcRedex = await findRedexApplicativeOrder(collection, term.func);
92
+ if (funcRedex.isJust)
93
+ return funcRedex;
94
+ const argRedex = await findRedexApplicativeOrder(collection, term.arg);
95
+ if (argRedex.isJust)
96
+ return argRedex;
97
+ // Then check if this is a redex
98
+ const func = await loadTerm(collection, term.func);
99
+ if (func && isAbs(func)) {
100
+ return Maybe.just(termHash);
101
+ }
102
+ return Maybe.nothing();
103
+ }
104
+ }
105
+ }
106
+ // ─────────────────────────────────────────────────────────────────────────────
107
+ // Beta Reduction
108
+ // ─────────────────────────────────────────────────────────────────────────────
109
+ /**
110
+ * Perform a single beta reduction step on a redex
111
+ *
112
+ * (λx.M) N →β M[x:=N]
113
+ *
114
+ * @param collection - Card collection
115
+ * @param redexHash - Hash of the application term (λx.M) N
116
+ * @returns Either<error, resultHash>
117
+ */
118
+ export function betaReduce(collection, redexHash) {
119
+ return IO.of(async () => {
120
+ const app = await loadTerm(collection, redexHash);
121
+ if (!app) {
122
+ return Either.left(`Term not found: ${redexHash}`);
123
+ }
124
+ if (!isApp(app)) {
125
+ return Either.left(`Beta reduction requires application term, got ${app.tag}`);
126
+ }
127
+ const func = await loadTerm(collection, app.func);
128
+ if (!func) {
129
+ return Either.left(`Function not found: ${app.func}`);
130
+ }
131
+ if (!isAbs(func)) {
132
+ return Either.left(`Not a beta-redex: function is ${func.tag}, not abstraction`);
133
+ }
134
+ // Perform substitution: M[x:=N]
135
+ const resultHash = await substituteWithCapture(collection, func.body, // M
136
+ func.param, // x
137
+ app.arg // N (as hash)
138
+ );
139
+ return Either.right(resultHash);
140
+ });
141
+ }
142
+ /**
143
+ * Substitute a variable with a term (by hash), handling capture avoidance
144
+ *
145
+ * M[x:=N] - replaces all free occurrences of x in M with N
146
+ */
147
+ async function substituteWithCapture(collection, termHash, variable, replacementHash) {
148
+ const term = await loadTerm(collection, termHash);
149
+ if (!term) {
150
+ throw new Error(`Term not found: ${termHash}`);
151
+ }
152
+ switch (term.tag) {
153
+ case 'Var':
154
+ // If this is the variable we're substituting, return the replacement
155
+ if (term.name === variable) {
156
+ return replacementHash;
157
+ }
158
+ // Otherwise unchanged
159
+ return termHash;
160
+ case 'Abs': {
161
+ // If bound variable shadows, stop substitution
162
+ if (term.param === variable) {
163
+ return termHash;
164
+ }
165
+ // Check for variable capture:
166
+ // If the bound variable (term.param) is free in the replacement,
167
+ // we must α-rename to avoid capture
168
+ const replacementFV = await freeVariables(collection, replacementHash).run();
169
+ if (replacementFV.isJust && replacementFV.value.has(term.param)) {
170
+ // Capture would occur! Need to α-rename first
171
+ const allVars = new Set(replacementFV.value);
172
+ // Also avoid variables in the body
173
+ const bodyFV = await freeVariables(collection, term.body).run();
174
+ if (bodyFV.isJust) {
175
+ for (const v of bodyFV.value)
176
+ allVars.add(v);
177
+ }
178
+ allVars.add(variable);
179
+ // Generate fresh name
180
+ const freshName = generateFresh(term.param, allVars);
181
+ // α-rename the abstraction
182
+ const renameResult = await alphaRename(collection, termHash, freshName).run();
183
+ if (renameResult.isLeft) {
184
+ throw new Error(`Alpha rename failed: ${renameResult.left}`);
185
+ }
186
+ // Now substitute in the renamed term
187
+ return substituteWithCapture(collection, renameResult.right, variable, replacementHash);
188
+ }
189
+ // No capture - recurse into body
190
+ const newBody = await substituteWithCapture(collection, term.body, variable, replacementHash);
191
+ if (newBody === term.body) {
192
+ return termHash;
193
+ }
194
+ const newAbs = mkAbs(term.param, newBody);
195
+ return storeTerm(collection, newAbs);
196
+ }
197
+ case 'App': {
198
+ const newFunc = await substituteWithCapture(collection, term.func, variable, replacementHash);
199
+ const newArg = await substituteWithCapture(collection, term.arg, variable, replacementHash);
200
+ if (newFunc === term.func && newArg === term.arg) {
201
+ return termHash;
202
+ }
203
+ const newApp = mkApp(newFunc, newArg);
204
+ return storeTerm(collection, newApp);
205
+ }
206
+ }
207
+ }
208
+ /**
209
+ * Perform one reduction step using the specified strategy
210
+ */
211
+ export function reduceStep(collection, termHash, strategy = 'normal') {
212
+ return IO.of(async () => {
213
+ // Find redex using strategy
214
+ let redexMaybe;
215
+ switch (strategy) {
216
+ case 'normal':
217
+ case 'lazy':
218
+ redexMaybe = await findRedexNormalOrder(collection, termHash);
219
+ break;
220
+ case 'applicative':
221
+ redexMaybe = await findRedexApplicativeOrder(collection, termHash);
222
+ break;
223
+ }
224
+ if (redexMaybe.isNothing) {
225
+ // No redex found - already in normal form
226
+ return Maybe.nothing();
227
+ }
228
+ const redexHash = redexMaybe.value;
229
+ // If the redex is the whole term, just reduce it
230
+ if (redexHash === termHash) {
231
+ const result = await betaReduce(collection, redexHash).run();
232
+ if (result.isLeft) {
233
+ throw new Error(result.left);
234
+ }
235
+ return Maybe.just(result.right);
236
+ }
237
+ // Otherwise, need to reduce the subterm and reconstruct
238
+ const reduced = await betaReduce(collection, redexHash).run();
239
+ if (reduced.isLeft) {
240
+ throw new Error(reduced.left);
241
+ }
242
+ const rebuilt = await rebuildWithReduced(collection, termHash, redexHash, reduced.right);
243
+ return Maybe.just(rebuilt);
244
+ });
245
+ }
246
+ /**
247
+ * Rebuild a term after reducing a subterm
248
+ */
249
+ async function rebuildWithReduced(collection, termHash, redexHash, reducedHash) {
250
+ if (termHash === redexHash) {
251
+ return reducedHash;
252
+ }
253
+ const term = await loadTerm(collection, termHash);
254
+ if (!term)
255
+ throw new Error(`Term not found: ${termHash}`);
256
+ switch (term.tag) {
257
+ case 'Var':
258
+ return termHash;
259
+ case 'Abs': {
260
+ const newBody = await rebuildWithReduced(collection, term.body, redexHash, reducedHash);
261
+ if (newBody === term.body)
262
+ return termHash;
263
+ const newAbs = mkAbs(term.param, newBody);
264
+ return storeTerm(collection, newAbs);
265
+ }
266
+ case 'App': {
267
+ const newFunc = await rebuildWithReduced(collection, term.func, redexHash, reducedHash);
268
+ const newArg = await rebuildWithReduced(collection, term.arg, redexHash, reducedHash);
269
+ if (newFunc === term.func && newArg === term.arg)
270
+ return termHash;
271
+ const newApp = mkApp(newFunc, newArg);
272
+ return storeTerm(collection, newApp);
273
+ }
274
+ }
275
+ }
276
+ /**
277
+ * Normalize a term to its normal form (no more redexes)
278
+ *
279
+ * @param collection - Card collection
280
+ * @param termHash - Starting term
281
+ * @param strategy - Reduction strategy
282
+ * @param maxSteps - Maximum reduction steps (prevent infinite loops)
283
+ * @returns Either<error, NormalizationResult>
284
+ */
285
+ export function normalize(collection, termHash, strategy = 'normal', maxSteps = 1000) {
286
+ return IO.of(async () => {
287
+ let current = termHash;
288
+ let steps = 0;
289
+ const path = [termHash];
290
+ while (steps < maxSteps) {
291
+ const stepResult = await reduceStep(collection, current, strategy).run();
292
+ if (stepResult.isNothing) {
293
+ // No more reductions - reached normal form
294
+ return Either.right({
295
+ normalForm: current,
296
+ steps,
297
+ reductionPath: path
298
+ });
299
+ }
300
+ current = stepResult.value;
301
+ path.push(current);
302
+ steps++;
303
+ }
304
+ return Either.left(`Normalization did not terminate within ${maxSteps} steps (possible infinite loop)`);
305
+ });
306
+ }
307
+ /**
308
+ * Check if a term is in normal form (has no redexes)
309
+ */
310
+ export function isNormalForm(collection, termHash) {
311
+ return findLeftmostRedex(collection, termHash).map(m => m.isNothing);
312
+ }
313
+ /**
314
+ * Check if a term has a normal form (is normalizing)
315
+ *
316
+ * Note: This is undecidable in general, so we use a bounded check
317
+ */
318
+ export function hasNormalForm(collection, termHash, maxSteps = 1000) {
319
+ return normalize(collection, termHash, 'normal', maxSteps)
320
+ .map(result => result.isRight);
321
+ }
322
+ //# sourceMappingURL=BetaReduction.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BetaReduction.js","sourceRoot":"","sources":["../../../src/ptr/lambda/BetaReduction.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Eta Conversion (η-conversion)
3
+ *
4
+ * Captures extensional equality of functions.
5
+ *
6
+ * η-reduction: λx.(f x) →η f (if x ∉ FV(f))
7
+ * η-expansion: f →η λx.(f x) (where x is fresh)
8
+ *
9
+ * Two functions are η-equivalent if they produce the same output for all inputs.
10
+ * This is the principle of extensionality: functions are determined by their behavior.
11
+ *
12
+ * @module mcard-js/ptr/lambda/EtaConversion
13
+ */
14
+ import { CardCollection } from '../../model/CardCollection';
15
+ import { IO } from '../../monads/IO';
16
+ import { Either } from '../../monads/Either';
17
+ import { Maybe } from '../../monads/Maybe';
18
+ /**
19
+ * Check if a term is an η-redex: λx.(f x) where x ∉ FV(f)
20
+ */
21
+ export declare function isEtaRedex(collection: CardCollection, termHash: string): IO<boolean>;
22
+ /**
23
+ * Eta reduction: λx.(f x) →η f
24
+ *
25
+ * Only valid if x does not occur free in f.
26
+ *
27
+ * @param collection - Card collection
28
+ * @param termHash - Hash of the abstraction λx.(f x)
29
+ * @returns Maybe<resultHash> - Nothing if not an η-redex
30
+ */
31
+ export declare function etaReduce(collection: CardCollection, termHash: string): IO<Maybe<string>>;
32
+ /**
33
+ * Try eta reduction, returning Either for error handling
34
+ */
35
+ export declare function etaReduceE(collection: CardCollection, termHash: string): IO<Either<string, string>>;
36
+ /**
37
+ * Eta expansion: f →η λx.(f x)
38
+ *
39
+ * Wraps a function in a lambda that immediately applies it.
40
+ * The new variable must be fresh (not occurring in f).
41
+ *
42
+ * @param collection - Card collection
43
+ * @param termHash - Hash of the term to expand
44
+ * @param baseName - Base name for the fresh variable (default: 'x')
45
+ * @returns Hash of the expanded term λx.(f x)
46
+ */
47
+ export declare function etaExpand(collection: CardCollection, termHash: string, baseName?: string): IO<string>;
48
+ /**
49
+ * Check if two terms are η-equivalent
50
+ *
51
+ * Two terms f and g are η-equivalent if:
52
+ * - They are the same, or
53
+ * - One η-reduces to the other, or
54
+ * - They both η-expand to equivalent terms
55
+ */
56
+ export declare function etaEquivalent(collection: CardCollection, hash1: string, hash2: string): IO<boolean>;
57
+ /**
58
+ * Eta-normalize a term by reducing all η-redexes
59
+ */
60
+ export declare function etaNormalize(collection: CardCollection, termHash: string): IO<string>;
61
+ /**
62
+ * Find all η-redexes in a term
63
+ */
64
+ export declare function findEtaRedexes(collection: CardCollection, termHash: string): IO<string[]>;
65
+ //# sourceMappingURL=EtaConversion.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EtaConversion.d.ts","sourceRoot":"","sources":["../../../src/ptr/lambda/EtaConversion.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAa5D,OAAO,EAAE,EAAE,EAAE,MAAM,iBAAiB,CAAC;AACrC,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAC7C,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAM3C;;GAEG;AACH,wBAAgB,UAAU,CACtB,UAAU,EAAE,cAAc,EAC1B,QAAQ,EAAE,MAAM,GACjB,EAAE,CAAC,OAAO,CAAC,CAkBb;AAMD;;;;;;;;GAQG;AACH,wBAAgB,SAAS,CACrB,UAAU,EAAE,cAAc,EAC1B,QAAQ,EAAE,MAAM,GACjB,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAiCnB;AAED;;GAEG;AACH,wBAAgB,UAAU,CACtB,UAAU,EAAE,cAAc,EAC1B,QAAQ,EAAE,MAAM,GACjB,EAAE,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAO5B;AAMD;;;;;;;;;;GAUG;AACH,wBAAgB,SAAS,CACrB,UAAU,EAAE,cAAc,EAC1B,QAAQ,EAAE,MAAM,EAChB,QAAQ,GAAE,MAAY,GACvB,EAAE,CAAC,MAAM,CAAC,CAmBZ;AAMD;;;;;;;GAOG;AACH,wBAAgB,aAAa,CACzB,UAAU,EAAE,cAAc,EAC1B,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,GACd,EAAE,CAAC,OAAO,CAAC,CAWb;AAgDD;;GAEG;AACH,wBAAgB,YAAY,CACxB,UAAU,EAAE,cAAc,EAC1B,QAAQ,EAAE,MAAM,GACjB,EAAE,CAAC,MAAM,CAAC,CAEZ;AAED;;GAEG;AACH,wBAAgB,cAAc,CAC1B,UAAU,EAAE,cAAc,EAC1B,QAAQ,EAAE,MAAM,GACjB,EAAE,CAAC,MAAM,EAAE,CAAC,CAMd"}
@@ -0,0 +1,228 @@
1
+ /**
2
+ * Eta Conversion (η-conversion)
3
+ *
4
+ * Captures extensional equality of functions.
5
+ *
6
+ * η-reduction: λx.(f x) →η f (if x ∉ FV(f))
7
+ * η-expansion: f →η λx.(f x) (where x is fresh)
8
+ *
9
+ * Two functions are η-equivalent if they produce the same output for all inputs.
10
+ * This is the principle of extensionality: functions are determined by their behavior.
11
+ *
12
+ * @module mcard-js/ptr/lambda/EtaConversion
13
+ */
14
+ import { loadTerm, storeTerm, mkVar, mkAbs, mkApp, isVar, isAbs, isApp } from './LambdaTerm';
15
+ import { freeVariables, generateFreshFor } from './FreeVariables';
16
+ import { IO } from '../../monads/IO';
17
+ import { Either } from '../../monads/Either';
18
+ import { Maybe } from '../../monads/Maybe';
19
+ // ─────────────────────────────────────────────────────────────────────────────
20
+ // Eta Redex Detection
21
+ // ─────────────────────────────────────────────────────────────────────────────
22
+ /**
23
+ * Check if a term is an η-redex: λx.(f x) where x ∉ FV(f)
24
+ */
25
+ export function isEtaRedex(collection, termHash) {
26
+ return IO.of(async () => {
27
+ const term = await loadTerm(collection, termHash);
28
+ if (!term || !isAbs(term))
29
+ return false;
30
+ const body = await loadTerm(collection, term.body);
31
+ if (!body || !isApp(body))
32
+ return false;
33
+ // Check if argument is the bound variable
34
+ const arg = await loadTerm(collection, body.arg);
35
+ if (!arg || !isVar(arg) || arg.name !== term.param)
36
+ return false;
37
+ // Check if x is not free in f
38
+ const funcFV = await freeVariables(collection, body.func).run();
39
+ if (funcFV.isNothing)
40
+ return false;
41
+ return !funcFV.value.has(term.param);
42
+ });
43
+ }
44
+ // ─────────────────────────────────────────────────────────────────────────────
45
+ // Eta Reduction
46
+ // ─────────────────────────────────────────────────────────────────────────────
47
+ /**
48
+ * Eta reduction: λx.(f x) →η f
49
+ *
50
+ * Only valid if x does not occur free in f.
51
+ *
52
+ * @param collection - Card collection
53
+ * @param termHash - Hash of the abstraction λx.(f x)
54
+ * @returns Maybe<resultHash> - Nothing if not an η-redex
55
+ */
56
+ export function etaReduce(collection, termHash) {
57
+ return IO.of(async () => {
58
+ const term = await loadTerm(collection, termHash);
59
+ if (!term)
60
+ return Maybe.nothing();
61
+ // Must be λx.E
62
+ if (!isAbs(term))
63
+ return Maybe.nothing();
64
+ const body = await loadTerm(collection, term.body);
65
+ if (!body)
66
+ return Maybe.nothing();
67
+ // Body must be application (f x)
68
+ if (!isApp(body))
69
+ return Maybe.nothing();
70
+ // Argument must be the bound variable
71
+ const arg = await loadTerm(collection, body.arg);
72
+ if (!arg)
73
+ return Maybe.nothing();
74
+ if (!isVar(arg) || arg.name !== term.param) {
75
+ return Maybe.nothing();
76
+ }
77
+ // x must not be free in f
78
+ const funcFV = await freeVariables(collection, body.func).run();
79
+ if (funcFV.isNothing)
80
+ return Maybe.nothing();
81
+ if (funcFV.value.has(term.param)) {
82
+ return Maybe.nothing();
83
+ }
84
+ // η-reduce: return f's hash
85
+ return Maybe.just(body.func);
86
+ });
87
+ }
88
+ /**
89
+ * Try eta reduction, returning Either for error handling
90
+ */
91
+ export function etaReduceE(collection, termHash) {
92
+ return etaReduce(collection, termHash).map(maybe => {
93
+ if (maybe.isNothing) {
94
+ return Either.left('Not an η-redex');
95
+ }
96
+ return Either.right(maybe.value);
97
+ });
98
+ }
99
+ // ─────────────────────────────────────────────────────────────────────────────
100
+ // Eta Expansion
101
+ // ─────────────────────────────────────────────────────────────────────────────
102
+ /**
103
+ * Eta expansion: f →η λx.(f x)
104
+ *
105
+ * Wraps a function in a lambda that immediately applies it.
106
+ * The new variable must be fresh (not occurring in f).
107
+ *
108
+ * @param collection - Card collection
109
+ * @param termHash - Hash of the term to expand
110
+ * @param baseName - Base name for the fresh variable (default: 'x')
111
+ * @returns Hash of the expanded term λx.(f x)
112
+ */
113
+ export function etaExpand(collection, termHash, baseName = 'x') {
114
+ return IO.of(async () => {
115
+ // Generate fresh variable name
116
+ const freshVar = await generateFreshFor(collection, baseName, termHash);
117
+ // Create variable term
118
+ const varTerm = mkVar(freshVar);
119
+ const varHash = await storeTerm(collection, varTerm);
120
+ // Create application (f x)
121
+ const appTerm = mkApp(termHash, varHash);
122
+ const appHash = await storeTerm(collection, appTerm);
123
+ // Create abstraction λx.(f x)
124
+ const absTerm = mkAbs(freshVar, appHash);
125
+ const absHash = await storeTerm(collection, absTerm);
126
+ return absHash;
127
+ });
128
+ }
129
+ // ─────────────────────────────────────────────────────────────────────────────
130
+ // Eta Equivalence
131
+ // ─────────────────────────────────────────────────────────────────────────────
132
+ /**
133
+ * Check if two terms are η-equivalent
134
+ *
135
+ * Two terms f and g are η-equivalent if:
136
+ * - They are the same, or
137
+ * - One η-reduces to the other, or
138
+ * - They both η-expand to equivalent terms
139
+ */
140
+ export function etaEquivalent(collection, hash1, hash2) {
141
+ return IO.of(async () => {
142
+ // Same hash = definitely equivalent
143
+ if (hash1 === hash2)
144
+ return true;
145
+ // Try reducing both to see if they meet
146
+ const reduced1 = await etaReduceDeep(collection, hash1);
147
+ const reduced2 = await etaReduceDeep(collection, hash2);
148
+ return reduced1 === reduced2;
149
+ });
150
+ }
151
+ /**
152
+ * Recursively apply η-reduction until no more η-redexes
153
+ */
154
+ async function etaReduceDeep(collection, termHash) {
155
+ const term = await loadTerm(collection, termHash);
156
+ if (!term)
157
+ return termHash;
158
+ switch (term.tag) {
159
+ case 'Var':
160
+ return termHash;
161
+ case 'Abs': {
162
+ // First try η-reduction at this level
163
+ const etaResult = await etaReduce(collection, termHash).run();
164
+ if (etaResult.isJust) {
165
+ // η-reduced - continue reducing the result
166
+ return etaReduceDeep(collection, etaResult.value);
167
+ }
168
+ // Not an η-redex - recurse into body
169
+ const newBody = await etaReduceDeep(collection, term.body);
170
+ if (newBody === term.body)
171
+ return termHash;
172
+ const newAbs = mkAbs(term.param, newBody);
173
+ return storeTerm(collection, newAbs);
174
+ }
175
+ case 'App': {
176
+ const newFunc = await etaReduceDeep(collection, term.func);
177
+ const newArg = await etaReduceDeep(collection, term.arg);
178
+ if (newFunc === term.func && newArg === term.arg)
179
+ return termHash;
180
+ const newApp = mkApp(newFunc, newArg);
181
+ return storeTerm(collection, newApp);
182
+ }
183
+ }
184
+ }
185
+ // ─────────────────────────────────────────────────────────────────────────────
186
+ // Combined Eta Operations
187
+ // ─────────────────────────────────────────────────────────────────────────────
188
+ /**
189
+ * Eta-normalize a term by reducing all η-redexes
190
+ */
191
+ export function etaNormalize(collection, termHash) {
192
+ return IO.of(() => etaReduceDeep(collection, termHash));
193
+ }
194
+ /**
195
+ * Find all η-redexes in a term
196
+ */
197
+ export function findEtaRedexes(collection, termHash) {
198
+ return IO.of(async () => {
199
+ const redexes = [];
200
+ await collectEtaRedexes(collection, termHash, redexes);
201
+ return redexes;
202
+ });
203
+ }
204
+ async function collectEtaRedexes(collection, termHash, redexes) {
205
+ const term = await loadTerm(collection, termHash);
206
+ if (!term)
207
+ return;
208
+ switch (term.tag) {
209
+ case 'Var':
210
+ break;
211
+ case 'Abs': {
212
+ // Check if this is an η-redex
213
+ const isRedex = await isEtaRedex(collection, termHash).run();
214
+ if (isRedex) {
215
+ redexes.push(termHash);
216
+ }
217
+ // Also check body
218
+ await collectEtaRedexes(collection, term.body, redexes);
219
+ break;
220
+ }
221
+ case 'App': {
222
+ await collectEtaRedexes(collection, term.func, redexes);
223
+ await collectEtaRedexes(collection, term.arg, redexes);
224
+ break;
225
+ }
226
+ }
227
+ }
228
+ //# sourceMappingURL=EtaConversion.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EtaConversion.js","sourceRoot":"","sources":["../../../src/ptr/lambda/EtaConversion.ts"],"names":[],"mappings":"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"}