@barnum/barnum 0.3.0 → 0.4.0

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 (109) hide show
  1. package/artifacts/linux-arm64/barnum +0 -0
  2. package/artifacts/linux-x64/barnum +0 -0
  3. package/artifacts/macos-arm64/barnum +0 -0
  4. package/artifacts/macos-x64/barnum +0 -0
  5. package/artifacts/win-x64/barnum.exe +0 -0
  6. package/dist/all.d.ts +41 -10
  7. package/dist/all.d.ts.map +1 -0
  8. package/dist/all.js +1 -1
  9. package/dist/ast.d.ts +199 -98
  10. package/dist/ast.d.ts.map +1 -0
  11. package/dist/ast.js +271 -233
  12. package/dist/bind.d.ts +9 -12
  13. package/dist/bind.d.ts.map +1 -0
  14. package/dist/bind.js +14 -51
  15. package/dist/builtins/array.d.ts +36 -0
  16. package/dist/builtins/array.d.ts.map +1 -0
  17. package/dist/builtins/array.js +93 -0
  18. package/dist/builtins/index.d.ts +6 -0
  19. package/dist/builtins/index.d.ts.map +1 -0
  20. package/dist/builtins/index.js +5 -0
  21. package/dist/builtins/scalar.d.ts +12 -0
  22. package/dist/builtins/scalar.d.ts.map +1 -0
  23. package/dist/builtins/scalar.js +41 -0
  24. package/dist/builtins/struct.d.ts +25 -0
  25. package/dist/builtins/struct.d.ts.map +1 -0
  26. package/dist/builtins/struct.js +67 -0
  27. package/dist/builtins/tagged-union.d.ts +54 -0
  28. package/dist/builtins/tagged-union.d.ts.map +1 -0
  29. package/dist/builtins/tagged-union.js +81 -0
  30. package/dist/builtins/with-resource.d.ts +23 -0
  31. package/dist/builtins/with-resource.d.ts.map +1 -0
  32. package/dist/builtins/with-resource.js +35 -0
  33. package/dist/chain.d.ts +1 -0
  34. package/dist/chain.d.ts.map +1 -0
  35. package/dist/chain.js +3 -3
  36. package/dist/effect-id.d.ts +1 -0
  37. package/dist/effect-id.d.ts.map +1 -0
  38. package/dist/handler.d.ts +7 -6
  39. package/dist/handler.d.ts.map +1 -0
  40. package/dist/handler.js +5 -21
  41. package/dist/index.d.ts +10 -6
  42. package/dist/index.d.ts.map +1 -0
  43. package/dist/index.js +4 -2
  44. package/dist/iterator.d.ts +32 -0
  45. package/dist/iterator.d.ts.map +1 -0
  46. package/dist/iterator.js +123 -0
  47. package/dist/option.d.ts +74 -0
  48. package/dist/option.d.ts.map +1 -0
  49. package/dist/option.js +141 -0
  50. package/dist/pipe.d.ts +11 -10
  51. package/dist/pipe.d.ts.map +1 -0
  52. package/dist/pipe.js +5 -4
  53. package/dist/race.d.ts +5 -4
  54. package/dist/race.d.ts.map +1 -0
  55. package/dist/race.js +17 -42
  56. package/dist/recursive.d.ts +9 -3
  57. package/dist/recursive.d.ts.map +1 -0
  58. package/dist/recursive.js +18 -13
  59. package/dist/result.d.ts +50 -0
  60. package/dist/result.d.ts.map +1 -0
  61. package/dist/result.js +117 -0
  62. package/dist/run.d.ts +9 -2
  63. package/dist/run.d.ts.map +1 -0
  64. package/dist/run.js +37 -20
  65. package/dist/runtime.d.ts +6 -0
  66. package/dist/runtime.d.ts.map +1 -0
  67. package/dist/runtime.js +7 -0
  68. package/dist/schema.d.ts +1 -0
  69. package/dist/schema.d.ts.map +1 -0
  70. package/dist/schemas.d.ts +5 -0
  71. package/dist/schemas.d.ts.map +1 -0
  72. package/dist/schemas.js +13 -0
  73. package/dist/try-catch.d.ts +2 -1
  74. package/dist/try-catch.d.ts.map +1 -0
  75. package/dist/try-catch.js +10 -9
  76. package/dist/values.d.ts +6 -0
  77. package/dist/values.d.ts.map +1 -0
  78. package/dist/values.js +12 -0
  79. package/dist/worker.d.ts +5 -1
  80. package/dist/worker.d.ts.map +1 -0
  81. package/dist/worker.js +15 -3
  82. package/package.json +8 -6
  83. package/src/all.ts +118 -74
  84. package/src/ast.ts +773 -350
  85. package/src/bind.ts +32 -62
  86. package/src/builtins/array.ts +121 -0
  87. package/src/builtins/index.ts +17 -0
  88. package/src/builtins/scalar.ts +49 -0
  89. package/src/builtins/struct.ts +111 -0
  90. package/src/builtins/tagged-union.ts +142 -0
  91. package/src/builtins/with-resource.ts +69 -0
  92. package/src/chain.ts +4 -4
  93. package/src/handler.ts +12 -28
  94. package/src/index.ts +24 -17
  95. package/src/iterator.ts +243 -0
  96. package/src/option.ts +199 -0
  97. package/src/pipe.ts +123 -78
  98. package/src/race.ts +41 -51
  99. package/src/recursive.ts +44 -27
  100. package/src/result.ts +168 -0
  101. package/src/run.ts +53 -25
  102. package/src/runtime.ts +16 -0
  103. package/src/schemas.ts +21 -0
  104. package/src/try-catch.ts +14 -10
  105. package/src/values.ts +21 -0
  106. package/src/worker.ts +17 -2
  107. package/dist/builtins.d.ts +0 -257
  108. package/dist/builtins.js +0 -600
  109. package/src/builtins.ts +0 -804
package/src/pipe.ts CHANGED
@@ -3,91 +3,136 @@ import {
3
3
  type PipeIn,
4
4
  type Pipeable,
5
5
  type TypedAction,
6
- typedAction,
6
+ toAction,
7
7
  } from "./ast.js";
8
- import { identity } from "./builtins.js";
8
+ import { chain } from "./chain.js";
9
+ import { identity } from "./builtins/index.js";
9
10
 
10
- export function pipe<T1, T2>(a1: Pipeable<T1, T2>): TypedAction<PipeIn<T1>, T2>;
11
- export function pipe<T1, T2, T3>(
12
- a1: Pipeable<T1, T2>,
13
- a2: Pipeable<T2, T3>,
14
- ): TypedAction<PipeIn<T1>, T3>;
15
- export function pipe<T1, T2, T3, T4>(
16
- a1: Pipeable<T1, T2>,
17
- a2: Pipeable<T2, T3>,
18
- a3: Pipeable<T3, T4>,
19
- ): TypedAction<PipeIn<T1>, T4>;
20
- export function pipe<T1, T2, T3, T4, T5>(
21
- a1: Pipeable<T1, T2>,
22
- a2: Pipeable<T2, T3>,
23
- a3: Pipeable<T3, T4>,
24
- a4: Pipeable<T4, T5>,
25
- ): TypedAction<PipeIn<T1>, T5>;
26
- export function pipe<T1, T2, T3, T4, T5, T6>(
27
- a1: Pipeable<T1, T2>,
28
- a2: Pipeable<T2, T3>,
29
- a3: Pipeable<T3, T4>,
30
- a4: Pipeable<T4, T5>,
31
- a5: Pipeable<T5, T6>,
32
- ): TypedAction<PipeIn<T1>, T6>;
33
- export function pipe<T1, T2, T3, T4, T5, T6, T7>(
34
- a1: Pipeable<T1, T2>,
35
- a2: Pipeable<T2, T3>,
36
- a3: Pipeable<T3, T4>,
37
- a4: Pipeable<T4, T5>,
38
- a5: Pipeable<T5, T6>,
39
- a6: Pipeable<T6, T7>,
40
- ): TypedAction<PipeIn<T1>, T7>;
41
- export function pipe<T1, T2, T3, T4, T5, T6, T7, T8>(
42
- a1: Pipeable<T1, T2>,
43
- a2: Pipeable<T2, T3>,
44
- a3: Pipeable<T3, T4>,
45
- a4: Pipeable<T4, T5>,
46
- a5: Pipeable<T5, T6>,
47
- a6: Pipeable<T6, T7>,
48
- a7: Pipeable<T7, T8>,
49
- ): TypedAction<PipeIn<T1>, T8>;
50
- export function pipe<T1, T2, T3, T4, T5, T6, T7, T8, T9>(
51
- a1: Pipeable<T1, T2>,
52
- a2: Pipeable<T2, T3>,
53
- a3: Pipeable<T3, T4>,
54
- a4: Pipeable<T4, T5>,
55
- a5: Pipeable<T5, T6>,
56
- a6: Pipeable<T6, T7>,
57
- a7: Pipeable<T7, T8>,
58
- a8: Pipeable<T8, T9>,
59
- ): TypedAction<PipeIn<T1>, T9>;
60
- export function pipe<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>(
61
- a1: Pipeable<T1, T2>,
62
- a2: Pipeable<T2, T3>,
63
- a3: Pipeable<T3, T4>,
64
- a4: Pipeable<T4, T5>,
65
- a5: Pipeable<T5, T6>,
66
- a6: Pipeable<T6, T7>,
67
- a7: Pipeable<T7, T8>,
68
- a8: Pipeable<T8, T9>,
69
- a9: Pipeable<T9, T10>,
70
- ): TypedAction<PipeIn<T1>, T10>;
71
- export function pipe<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11>(
72
- a1: Pipeable<T1, T2>,
73
- a2: Pipeable<T2, T3>,
74
- a3: Pipeable<T3, T4>,
75
- a4: Pipeable<T4, T5>,
76
- a5: Pipeable<T5, T6>,
77
- a6: Pipeable<T6, T7>,
78
- a7: Pipeable<T7, T8>,
79
- a8: Pipeable<T8, T9>,
80
- a9: Pipeable<T9, T10>,
81
- a10: Pipeable<T10, T11>,
82
- ): TypedAction<PipeIn<T1>, T11>;
11
+ export function pipe<TStep1, TStep2>(
12
+ a1: Pipeable<TStep1, TStep2>,
13
+ ): TypedAction<PipeIn<TStep1>, TStep2>;
14
+ export function pipe<TStep1, TStep2, TStep3>(
15
+ a1: Pipeable<TStep1, TStep2>,
16
+ a2: Pipeable<TStep2, TStep3>,
17
+ ): TypedAction<PipeIn<TStep1>, TStep3>;
18
+ export function pipe<TStep1, TStep2, TStep3, TStep4>(
19
+ a1: Pipeable<TStep1, TStep2>,
20
+ a2: Pipeable<TStep2, TStep3>,
21
+ a3: Pipeable<TStep3, TStep4>,
22
+ ): TypedAction<PipeIn<TStep1>, TStep4>;
23
+ export function pipe<TStep1, TStep2, TStep3, TStep4, TStep5>(
24
+ a1: Pipeable<TStep1, TStep2>,
25
+ a2: Pipeable<TStep2, TStep3>,
26
+ a3: Pipeable<TStep3, TStep4>,
27
+ a4: Pipeable<TStep4, TStep5>,
28
+ ): TypedAction<PipeIn<TStep1>, TStep5>;
29
+ export function pipe<TStep1, TStep2, TStep3, TStep4, TStep5, TStep6>(
30
+ a1: Pipeable<TStep1, TStep2>,
31
+ a2: Pipeable<TStep2, TStep3>,
32
+ a3: Pipeable<TStep3, TStep4>,
33
+ a4: Pipeable<TStep4, TStep5>,
34
+ a5: Pipeable<TStep5, TStep6>,
35
+ ): TypedAction<PipeIn<TStep1>, TStep6>;
36
+ export function pipe<TStep1, TStep2, TStep3, TStep4, TStep5, TStep6, TStep7>(
37
+ a1: Pipeable<TStep1, TStep2>,
38
+ a2: Pipeable<TStep2, TStep3>,
39
+ a3: Pipeable<TStep3, TStep4>,
40
+ a4: Pipeable<TStep4, TStep5>,
41
+ a5: Pipeable<TStep5, TStep6>,
42
+ a6: Pipeable<TStep6, TStep7>,
43
+ ): TypedAction<PipeIn<TStep1>, TStep7>;
44
+ export function pipe<
45
+ TStep1,
46
+ TStep2,
47
+ TStep3,
48
+ TStep4,
49
+ TStep5,
50
+ TStep6,
51
+ TStep7,
52
+ TStep8,
53
+ >(
54
+ a1: Pipeable<TStep1, TStep2>,
55
+ a2: Pipeable<TStep2, TStep3>,
56
+ a3: Pipeable<TStep3, TStep4>,
57
+ a4: Pipeable<TStep4, TStep5>,
58
+ a5: Pipeable<TStep5, TStep6>,
59
+ a6: Pipeable<TStep6, TStep7>,
60
+ a7: Pipeable<TStep7, TStep8>,
61
+ ): TypedAction<PipeIn<TStep1>, TStep8>;
62
+ export function pipe<
63
+ TStep1,
64
+ TStep2,
65
+ TStep3,
66
+ TStep4,
67
+ TStep5,
68
+ TStep6,
69
+ TStep7,
70
+ TStep8,
71
+ TStep9,
72
+ >(
73
+ a1: Pipeable<TStep1, TStep2>,
74
+ a2: Pipeable<TStep2, TStep3>,
75
+ a3: Pipeable<TStep3, TStep4>,
76
+ a4: Pipeable<TStep4, TStep5>,
77
+ a5: Pipeable<TStep5, TStep6>,
78
+ a6: Pipeable<TStep6, TStep7>,
79
+ a7: Pipeable<TStep7, TStep8>,
80
+ a8: Pipeable<TStep8, TStep9>,
81
+ ): TypedAction<PipeIn<TStep1>, TStep9>;
82
+ export function pipe<
83
+ TStep1,
84
+ TStep2,
85
+ TStep3,
86
+ TStep4,
87
+ TStep5,
88
+ TStep6,
89
+ TStep7,
90
+ TStep8,
91
+ TStep9,
92
+ TStep10,
93
+ >(
94
+ a1: Pipeable<TStep1, TStep2>,
95
+ a2: Pipeable<TStep2, TStep3>,
96
+ a3: Pipeable<TStep3, TStep4>,
97
+ a4: Pipeable<TStep4, TStep5>,
98
+ a5: Pipeable<TStep5, TStep6>,
99
+ a6: Pipeable<TStep6, TStep7>,
100
+ a7: Pipeable<TStep7, TStep8>,
101
+ a8: Pipeable<TStep8, TStep9>,
102
+ a9: Pipeable<TStep9, TStep10>,
103
+ ): TypedAction<PipeIn<TStep1>, TStep10>;
104
+ export function pipe<
105
+ TStep1,
106
+ TStep2,
107
+ TStep3,
108
+ TStep4,
109
+ TStep5,
110
+ TStep6,
111
+ TStep7,
112
+ TStep8,
113
+ TStep9,
114
+ TStep10,
115
+ TStep11,
116
+ >(
117
+ a1: Pipeable<TStep1, TStep2>,
118
+ a2: Pipeable<TStep2, TStep3>,
119
+ a3: Pipeable<TStep3, TStep4>,
120
+ a4: Pipeable<TStep4, TStep5>,
121
+ a5: Pipeable<TStep5, TStep6>,
122
+ a6: Pipeable<TStep6, TStep7>,
123
+ a7: Pipeable<TStep7, TStep8>,
124
+ a8: Pipeable<TStep8, TStep9>,
125
+ a9: Pipeable<TStep9, TStep10>,
126
+ a10: Pipeable<TStep10, TStep11>,
127
+ ): TypedAction<PipeIn<TStep1>, TStep11>;
83
128
  export function pipe(...actions: Action[]): Action {
84
129
  if (actions.length === 0) {
85
- return identity;
130
+ return identity();
86
131
  }
87
132
  if (actions.length === 1) {
88
133
  return actions[0];
89
134
  }
90
- return actions.reduceRight(
91
- (rest, first) => typedAction({ kind: "Chain", first, rest }) as Action,
135
+ return actions.reduceRight((rest, first) =>
136
+ toAction(chain(toAction(first), toAction(rest))),
92
137
  );
93
138
  }
package/src/race.ts CHANGED
@@ -1,43 +1,32 @@
1
1
  import {
2
2
  type Action,
3
3
  type Pipeable,
4
- type Result,
4
+ type Result as ResultT,
5
5
  type TypedAction,
6
+ toAction,
6
7
  typedAction,
7
8
  buildRestartBranchAction,
8
- TAG_BREAK,
9
- IDENTITY,
10
9
  } from "./ast.js";
10
+ import { chain } from "./chain.js";
11
+ import { identity, tag } from "./builtins/index.js";
12
+ import { Result } from "./result.js";
11
13
  import {
12
14
  allocateRestartHandlerId,
13
15
  type RestartHandlerId,
14
16
  } from "./effect-id.js";
15
17
 
16
- // ---------------------------------------------------------------------------
17
- // Shared AST fragments
18
- // ---------------------------------------------------------------------------
19
-
20
- const TAG_OK: Action = {
21
- kind: "Invoke",
22
- handler: { kind: "Builtin", builtin: { kind: "Tag", value: "Ok" } },
23
- };
24
-
25
- const TAG_ERR: Action = {
26
- kind: "Invoke",
27
- handler: { kind: "Builtin", builtin: { kind: "Tag", value: "Err" } },
28
- };
29
-
30
18
  /**
31
19
  * `Chain(Tag("Break"), RestartPerform(id))` — shared by race branches.
32
20
  * The winning branch tags its result as Break, then performs. The handler
33
21
  * restarts the body; Branch takes the Break arm (identity), `RestartHandle` exits.
34
22
  */
35
23
  function breakPerform(restartHandlerId: RestartHandlerId): Action {
36
- return {
37
- kind: "Chain",
38
- first: TAG_BREAK,
39
- rest: { kind: "RestartPerform", restart_handler_id: restartHandlerId },
40
- };
24
+ return toAction(
25
+ chain(toAction(tag("Break", "LoopResult")), {
26
+ kind: "RestartPerform",
27
+ restart_handler_id: restartHandlerId,
28
+ }),
29
+ );
41
30
  }
42
31
 
43
32
  // ---------------------------------------------------------------------------
@@ -53,7 +42,7 @@ function breakPerform(restartHandlerId: RestartHandlerId): Action {
53
42
  *
54
43
  * Compiled form (restart+Branch, same substrate as loop/earlyReturn):
55
44
  * `Chain(Tag("Continue"),`
56
- * `RestartHandle(id, ExtractIndex(0),`
45
+ * `RestartHandle(id, GetIndex(0),`
57
46
  * `Branch({`
58
47
  * `Continue: All(Chain(a, breakPerform), Chain(b, breakPerform), ...),`
59
48
  * `Break: identity,`
@@ -68,16 +57,14 @@ export function race<TIn, TOut>(
68
57
  const restartHandlerId = allocateRestartHandlerId();
69
58
  const perform = breakPerform(restartHandlerId);
70
59
 
71
- const branches = actions.map((action) => ({
72
- kind: "Chain" as const,
73
- first: action as Action,
74
- rest: perform,
75
- }));
60
+ const branches = actions.map((action) =>
61
+ toAction(chain(toAction(action), toAction(perform))),
62
+ );
76
63
 
77
64
  const allAction: Action = { kind: "All", actions: branches };
78
65
 
79
66
  return typedAction(
80
- buildRestartBranchAction(restartHandlerId, allAction, IDENTITY),
67
+ buildRestartBranchAction(restartHandlerId, allAction, toAction(identity())),
81
68
  );
82
69
  }
83
70
 
@@ -93,10 +80,10 @@ export function race<TIn, TOut>(
93
80
  *
94
81
  * To preserve data across a sleep, use `bindInput`.
95
82
  */
96
- export function sleep(ms: number): TypedAction<any, never> {
97
- return typedAction<any, never>({
83
+ export function sleep(ms: number): TypedAction<any, void> {
84
+ return typedAction<any, void>({
98
85
  kind: "Invoke",
99
- handler: { kind: "Builtin", builtin: { kind: "Sleep", value: ms } },
86
+ handler: { kind: "Builtin", builtin: { kind: "Sleep", ms } },
100
87
  });
101
88
  }
102
89
 
@@ -153,31 +140,34 @@ Object.defineProperty(dynamicSleep, "__definition", {
153
140
  export function withTimeout<TIn, TOut>(
154
141
  ms: Pipeable<TIn, number>,
155
142
  body: Pipeable<TIn, TOut>,
156
- ): TypedAction<TIn, Result<TOut, void>> {
143
+ ): TypedAction<TIn, ResultT<TOut, void>> {
157
144
  const restartHandlerId = allocateRestartHandlerId();
158
145
  const perform = breakPerform(restartHandlerId);
159
146
 
160
- // Branch 1: body → Tag("Ok") → Tag("Break") → RestartPerform
161
- const bodyBranch: Action = {
162
- kind: "Chain",
163
- first: { kind: "Chain", first: body as Action, rest: TAG_OK },
164
- rest: perform,
165
- };
166
-
167
- // Branch 2: ms → sleep() → Tag("Err") → Tag("Break") → RestartPerform
168
- const sleepBranch: Action = {
169
- kind: "Chain",
170
- first: {
171
- kind: "Chain",
172
- first: { kind: "Chain", first: ms as Action, rest: DYNAMIC_SLEEP_INVOKE },
173
- rest: TAG_ERR,
174
- },
175
- rest: perform,
176
- };
147
+ // Branch 1: body → Tag("Ok") → Break → RestartPerform
148
+ const bodyBranch = toAction(
149
+ chain(
150
+ toAction(chain(toAction(body), toAction(Result.ok()))),
151
+ toAction(perform),
152
+ ),
153
+ );
154
+
155
+ // Branch 2: ms sleep() → Tag("Err") → Break → RestartPerform
156
+ const sleepBranch = toAction(
157
+ chain(
158
+ toAction(
159
+ chain(
160
+ toAction(chain(toAction(ms), toAction(DYNAMIC_SLEEP_INVOKE))),
161
+ toAction(Result.err()),
162
+ ),
163
+ ),
164
+ toAction(perform),
165
+ ),
166
+ );
177
167
 
178
168
  const allAction: Action = { kind: "All", actions: [bodyBranch, sleepBranch] };
179
169
 
180
170
  return typedAction(
181
- buildRestartBranchAction(restartHandlerId, allAction, IDENTITY),
171
+ buildRestartBranchAction(restartHandlerId, allAction, toAction(identity())),
182
172
  );
183
173
  }
package/src/recursive.ts CHANGED
@@ -2,6 +2,7 @@ import {
2
2
  type Action,
3
3
  type Pipeable,
4
4
  type TypedAction,
5
+ toAction,
5
6
  typedAction,
6
7
  branch,
7
8
  } from "./ast.js";
@@ -10,10 +11,10 @@ import { chain } from "./chain.js";
10
11
  import {
11
12
  constant,
12
13
  identity,
13
- extractField,
14
- extractIndex,
14
+ getField,
15
+ getIndex,
15
16
  tag,
16
- } from "./builtins.js";
17
+ } from "./builtins/index.js";
17
18
  import { allocateResumeHandlerId } from "./effect-id.js";
18
19
 
19
20
  // ---------------------------------------------------------------------------
@@ -28,16 +29,14 @@ type FunctionRefs<TDefs extends FunctionDef[]> = {
28
29
 
29
30
  /**
30
31
  * Constraint for the entry-point callback return type. Only requires the
31
- * output phantom fields — omits __in and __in_co so that actions with
32
- * In = never (e.g. pipelines starting from a call token) are assignable.
32
+ * output phantom field — omits __in and __in_co so that actions with
33
+ * any input type (e.g. pipelines starting from a call token) are assignable.
33
34
  */
34
35
  type BodyResult<TOut> = Action & {
35
36
  __out?: () => TOut;
36
- __out_contra?: (output: TOut) => void;
37
37
  };
38
38
 
39
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
40
- const UNUSED_STATE: any = undefined;
39
+ const UNUSED_STATE = null;
41
40
 
42
41
  // ---------------------------------------------------------------------------
43
42
  // defineRecursiveFunctions
@@ -58,6 +57,12 @@ const UNUSED_STATE: any = undefined;
58
57
  * is Chain(Tag("CallN"), ResumePerform(id)). The handler dispatches to the
59
58
  * correct function body by tag. The caller's pipeline is preserved as a
60
59
  * ResumePerformFrame across each call.
60
+ *
61
+ * **Known limitation:** concurrent calls to the same function do not work
62
+ * as expected. `all(f(x), f(x))` will NOT call `f` twice — both branches
63
+ * perform on the same ResumeHandle, and the second perform will not
64
+ * execute independently. Use sequential calls (chain/then) instead of
65
+ * concurrent calls (all) when calling recursive functions multiple times.
61
66
  */
62
67
  export function defineRecursiveFunctions<TDefs extends FunctionDef[]>(
63
68
  bodiesFn: (...fns: FunctionRefs<TDefs>) => {
@@ -76,37 +81,49 @@ export function defineRecursiveFunctions<TDefs extends FunctionDef[]>(
76
81
  // Call tokens: Chain(Tag("CallN"), ResumePerform(resumeHandlerId))
77
82
  const fnCount = bodiesFn.length;
78
83
  const callTokens = Array.from({ length: fnCount }, (_, i) =>
79
- typedAction(chain(tag(`Call${i}`), resumePerform as any) as Action),
84
+ typedAction(
85
+ toAction(
86
+ chain(
87
+ toAction(tag(`Call${i}`, "RecursiveDispatch")),
88
+ toAction(resumePerform),
89
+ ),
90
+ ),
91
+ ),
80
92
  );
81
93
 
82
94
  // Get function body ASTs
83
- const bodyActions = bodiesFn(
84
- ...(callTokens as FunctionRefs<TDefs>),
85
- ) as Action[];
95
+ const bodyActions = (
96
+ bodiesFn(...(callTokens as FunctionRefs<TDefs>)) as Pipeable[]
97
+ ).map(toAction);
86
98
 
87
- // Branch cases: CallN → ExtractField("value") → bodyN
99
+ // Branch cases: CallN → GetField("value") → bodyN
88
100
  const cases: Record<string, Action> = {};
89
101
  for (let i = 0; i < bodyActions.length; i++) {
90
- cases[`Call${i}`] = chain(
91
- extractField("value"),
92
- bodyActions[i] as any,
93
- ) as Action;
102
+ cases[`Call${i}`] = toAction(
103
+ chain(toAction(getField("value")), toAction(bodyActions[i])),
104
+ );
94
105
  }
95
106
 
96
107
  // Return curried entry-point combinator
97
108
  return <TOut>(entryFn: (...fns: FunctionRefs<TDefs>) => BodyResult<TOut>) => {
98
- const userBody = entryFn(...(callTokens as FunctionRefs<TDefs>)) as Action;
109
+ const userBody = toAction(entryFn(...(callTokens as FunctionRefs<TDefs>)));
99
110
 
100
111
  return typedAction<any, TOut>(
101
- chain(all(identity, constant(UNUSED_STATE)), {
102
- kind: "ResumeHandle",
103
- resume_handler_id: resumeHandlerId,
104
- body: chain(extractIndex(0), userBody as any) as Action,
105
- handler: all(
106
- chain(extractIndex(0), branch(cases) as any),
107
- constant(UNUSED_STATE),
108
- ) as Action,
109
- } as Action) as Action,
112
+ toAction(
113
+ chain(toAction(all(identity(), constant(UNUSED_STATE))), {
114
+ kind: "ResumeHandle",
115
+ resume_handler_id: resumeHandlerId,
116
+ body: toAction(
117
+ chain(toAction(getIndex(0).unwrap()), toAction(userBody)),
118
+ ),
119
+ handler: toAction(
120
+ all(
121
+ chain(toAction(getIndex(0).unwrap()), toAction(branch(cases))),
122
+ constant(UNUSED_STATE),
123
+ ),
124
+ ),
125
+ }),
126
+ ),
110
127
  );
111
128
  };
112
129
  }
package/src/result.ts ADDED
@@ -0,0 +1,168 @@
1
+ import {
2
+ type Option as OptionT,
3
+ type Pipeable,
4
+ type Result as ResultT,
5
+ type ResultDef,
6
+ type TypedAction,
7
+ branch,
8
+ } from "./ast.js";
9
+ import { chain } from "./chain.js";
10
+ import { constant, drop, identity, panic, tag } from "./builtins/index.js";
11
+ import { Option } from "./option.js";
12
+
13
+ // ---------------------------------------------------------------------------
14
+ // Result namespace — combinators for Result<TValue, TError> tagged unions
15
+ // ---------------------------------------------------------------------------
16
+
17
+ export const Result = {
18
+ /** Tag combinator: wrap value as `Result.Ok`. `TValue → Result<TValue, TError>` */
19
+ ok<TValue, TError = never>(): TypedAction<TValue, ResultT<TValue, TError>> {
20
+ return tag<"Result", ResultDef<TValue, TError>, "Ok">("Ok", "Result");
21
+ },
22
+ /** Tag combinator: wrap value as `Result.Err`. `TError → Result<TValue, TError>` */
23
+ err<TValue = never, TError = unknown>(): TypedAction<
24
+ TError,
25
+ ResultT<TValue, TError>
26
+ > {
27
+ return tag<"Result", ResultDef<TValue, TError>, "Err">("Err", "Result");
28
+ },
29
+
30
+ /** Transform the Ok value. `Result<TValue, TError> → Result<TOut, TError>` */
31
+ map<TValue, TOut, TError>(
32
+ action: Pipeable<TValue, TOut>,
33
+ ): TypedAction<ResultT<TValue, TError>, ResultT<TOut, TError>> {
34
+ return branch({
35
+ Ok: chain(action, Result.ok<TOut, TError>()),
36
+ Err: Result.err<TOut, TError>(),
37
+ }) as TypedAction<ResultT<TValue, TError>, ResultT<TOut, TError>>;
38
+ },
39
+
40
+ /** Transform the Err value. `Result<TValue, TError> → Result<TValue, TErrorOut>` */
41
+ mapErr<TValue, TError, TErrorOut>(
42
+ action: Pipeable<TError, TErrorOut>,
43
+ ): TypedAction<ResultT<TValue, TError>, ResultT<TValue, TErrorOut>> {
44
+ return branch({
45
+ Ok: Result.ok<TValue, TErrorOut>(),
46
+ Err: chain(action, Result.err<TValue, TErrorOut>()),
47
+ }) as TypedAction<ResultT<TValue, TError>, ResultT<TValue, TErrorOut>>;
48
+ },
49
+
50
+ /**
51
+ * Monadic bind (flatMap) for Ok. If Ok, pass value to action which
52
+ * returns Result<TOut, TError>. If Err, propagate.
53
+ */
54
+ andThen<TValue, TOut, TError>(
55
+ action: Pipeable<TValue, ResultT<TOut, TError>>,
56
+ ): TypedAction<ResultT<TValue, TError>, ResultT<TOut, TError>> {
57
+ return branch({
58
+ Ok: action,
59
+ Err: Result.err<TOut, TError>(),
60
+ }) as TypedAction<ResultT<TValue, TError>, ResultT<TOut, TError>>;
61
+ },
62
+
63
+ /** Fallback on Err. If Ok, keep it. If Err, pass error to fallback. */
64
+ or<TValue, TError, TErrorOut>(
65
+ fallback: Pipeable<TError, ResultT<TValue, TErrorOut>>,
66
+ ): TypedAction<ResultT<TValue, TError>, ResultT<TValue, TErrorOut>> {
67
+ return branch({
68
+ Ok: Result.ok<TValue, TErrorOut>(),
69
+ Err: fallback,
70
+ }) as TypedAction<ResultT<TValue, TError>, ResultT<TValue, TErrorOut>>;
71
+ },
72
+
73
+ /**
74
+ * Extract the Ok value or panic. `Result<TValue, TError> → TValue`
75
+ *
76
+ * Panics (fatal, not caught by tryCatch) if the value is Err.
77
+ */
78
+ unwrap<TValue, TError>(): TypedAction<ResultT<TValue, TError>, TValue> {
79
+ return branch({
80
+ Ok: identity<TValue>(),
81
+ Err: panic("called unwrap on Err"),
82
+ }) as TypedAction<ResultT<TValue, TError>, TValue>;
83
+ },
84
+
85
+ /**
86
+ * Extract Ok or compute default from Err. `Result<TValue, TError> → TValue`
87
+ */
88
+ unwrapOr<TValue, TError>(
89
+ defaultAction: Pipeable<TError, TValue>,
90
+ ): TypedAction<ResultT<TValue, TError>, TValue> {
91
+ return branch({
92
+ Ok: identity<TValue>(),
93
+ Err: defaultAction,
94
+ }) as TypedAction<ResultT<TValue, TError>, TValue>;
95
+ },
96
+
97
+ /**
98
+ * Convert Ok to Some, Err to None. `Result<TValue, TError> → Option<TValue>`
99
+ */
100
+ asOkOption<TValue, TError>(): TypedAction<
101
+ ResultT<TValue, TError>,
102
+ OptionT<TValue>
103
+ > {
104
+ return branch({
105
+ Ok: Option.some<TValue>(),
106
+ Err: chain(drop, Option.none<TValue>()),
107
+ }) as TypedAction<ResultT<TValue, TError>, OptionT<TValue>>;
108
+ },
109
+
110
+ /**
111
+ * Convert Err to Some, Ok to None. `Result<TValue, TError> → Option<TError>`
112
+ */
113
+ asErrOption<TValue, TError>(): TypedAction<
114
+ ResultT<TValue, TError>,
115
+ OptionT<TError>
116
+ > {
117
+ return branch({
118
+ Ok: chain(drop, Option.none<TError>()),
119
+ Err: Option.some<TError>(),
120
+ }) as TypedAction<ResultT<TValue, TError>, OptionT<TError>>;
121
+ },
122
+
123
+ /**
124
+ * Swap Result/Option nesting.
125
+ * `Result<Option<TValue>, TError> → Option<Result<TValue, TError>>`
126
+ */
127
+ transpose<TValue, TError>(): TypedAction<
128
+ ResultT<OptionT<TValue>, TError>,
129
+ OptionT<ResultT<TValue, TError>>
130
+ > {
131
+ return branch({
132
+ Ok: branch({
133
+ Some: chain(
134
+ Result.ok<TValue, TError>(),
135
+ Option.some<ResultT<TValue, TError>>(),
136
+ ),
137
+ None: chain(drop, Option.none<ResultT<TValue, TError>>()),
138
+ }),
139
+ Err: chain(
140
+ Result.err<TValue, TError>(),
141
+ Option.some<ResultT<TValue, TError>>(),
142
+ ),
143
+ }) as TypedAction<
144
+ ResultT<OptionT<TValue>, TError>,
145
+ OptionT<ResultT<TValue, TError>>
146
+ >;
147
+ },
148
+
149
+ /**
150
+ * Test if the value is Ok. `Result<TValue, TError> → boolean`
151
+ */
152
+ isOk<TValue, TError>(): TypedAction<ResultT<TValue, TError>, boolean> {
153
+ return branch({
154
+ Ok: constant<boolean>(true),
155
+ Err: constant<boolean>(false),
156
+ }) as TypedAction<ResultT<TValue, TError>, boolean>;
157
+ },
158
+
159
+ /**
160
+ * Test if the value is Err. `Result<TValue, TError> → boolean`
161
+ */
162
+ isErr<TValue, TError>(): TypedAction<ResultT<TValue, TError>, boolean> {
163
+ return branch({
164
+ Ok: constant<boolean>(false),
165
+ Err: constant<boolean>(true),
166
+ }) as TypedAction<ResultT<TValue, TError>, boolean>;
167
+ },
168
+ } as const;