@barnum/barnum 0.0.0-main-330fcf0a → 0.0.0-main-e8b82cff
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.
- package/artifacts/linux-arm64/barnum +0 -0
- package/artifacts/linux-x64/barnum +0 -0
- package/artifacts/macos-arm64/barnum +0 -0
- package/artifacts/macos-x64/barnum +0 -0
- package/artifacts/win-x64/barnum.exe +0 -0
- package/package.json +2 -1
- package/src/all.ts +116 -26
- package/src/ast.ts +262 -454
- package/src/bind.ts +66 -31
- package/src/builtins.ts +229 -132
- package/src/chain.ts +11 -2
- package/src/effect-id.ts +21 -6
- package/src/handler.ts +75 -32
- package/src/index.ts +11 -3
- package/src/pipe.ts +125 -30
- package/src/race.ts +58 -52
- package/src/run.ts +88 -20
- package/src/schema.ts +118 -0
- package/src/try-catch.ts +29 -30
- package/src/worker.ts +10 -1
package/src/builtins.ts
CHANGED
|
@@ -1,4 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
type Action,
|
|
3
|
+
type Option as OptionT,
|
|
4
|
+
type Pipeable,
|
|
5
|
+
type Result as ResultT,
|
|
6
|
+
type TaggedUnion,
|
|
7
|
+
type TypedAction,
|
|
8
|
+
typedAction,
|
|
9
|
+
} from "./ast.js";
|
|
2
10
|
import { chain } from "./chain.js";
|
|
3
11
|
|
|
4
12
|
/**
|
|
@@ -23,12 +31,10 @@ export function constant<TValue>(value: TValue): TypedAction<any, TValue> {
|
|
|
23
31
|
// Identity — pass input through unchanged
|
|
24
32
|
// ---------------------------------------------------------------------------
|
|
25
33
|
|
|
26
|
-
export
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
});
|
|
31
|
-
}
|
|
34
|
+
export const identity: TypedAction<any, any> = typedAction({
|
|
35
|
+
kind: "Invoke",
|
|
36
|
+
handler: { kind: "Builtin", builtin: { kind: "Identity" } },
|
|
37
|
+
});
|
|
32
38
|
|
|
33
39
|
// ---------------------------------------------------------------------------
|
|
34
40
|
// Drop — discard pipeline value
|
|
@@ -53,9 +59,7 @@ export const drop: TypedAction<any, never> = typedAction({
|
|
|
53
59
|
export function tag<
|
|
54
60
|
TDef extends Record<string, unknown>,
|
|
55
61
|
TKind extends keyof TDef & string,
|
|
56
|
-
>(
|
|
57
|
-
kind: TKind,
|
|
58
|
-
): TypedAction<TDef[TKind], TaggedUnion<TDef>> {
|
|
62
|
+
>(kind: TKind): TypedAction<TDef[TKind], TaggedUnion<TDef>> {
|
|
59
63
|
return typedAction({
|
|
60
64
|
kind: "Invoke",
|
|
61
65
|
handler: { kind: "Builtin", builtin: { kind: "Tag", value: kind } },
|
|
@@ -114,10 +118,9 @@ export function extractField<
|
|
|
114
118
|
// ExtractIndex — extract a single element from an array by index
|
|
115
119
|
// ---------------------------------------------------------------------------
|
|
116
120
|
|
|
117
|
-
export function extractIndex<
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
>(index: TIndex): TypedAction<TTuple, TTuple[TIndex]> {
|
|
121
|
+
export function extractIndex<TTuple extends unknown[], TIndex extends number>(
|
|
122
|
+
index: TIndex,
|
|
123
|
+
): TypedAction<TTuple, TTuple[TIndex]> {
|
|
121
124
|
return typedAction({
|
|
122
125
|
kind: "Invoke",
|
|
123
126
|
handler: {
|
|
@@ -153,7 +156,10 @@ export function dropResult<TInput, TOutput, TRefs extends string = never>(
|
|
|
153
156
|
return typedAction({
|
|
154
157
|
kind: "Chain",
|
|
155
158
|
first: action as Action,
|
|
156
|
-
rest: {
|
|
159
|
+
rest: {
|
|
160
|
+
kind: "Invoke",
|
|
161
|
+
handler: { kind: "Builtin", builtin: { kind: "Drop" } },
|
|
162
|
+
},
|
|
157
163
|
});
|
|
158
164
|
}
|
|
159
165
|
|
|
@@ -200,20 +206,26 @@ export function withResource<
|
|
|
200
206
|
const acquireAndMerge = chain(
|
|
201
207
|
typedAction<TIn, [TResource, TIn]>({
|
|
202
208
|
kind: "All",
|
|
203
|
-
actions: [create as Action, identity
|
|
209
|
+
actions: [create as Action, identity as Action],
|
|
204
210
|
}),
|
|
205
211
|
typedAction<[TResource, TIn], TResource & TIn>(mergeBuiltin),
|
|
206
212
|
);
|
|
207
213
|
|
|
208
214
|
// Step 2: all(action, identity) → [TOut, TResource & TIn]
|
|
209
215
|
// Keep merged object so dispose can access resource fields.
|
|
210
|
-
const actionAndKeepMerged = typedAction<
|
|
216
|
+
const actionAndKeepMerged = typedAction<
|
|
217
|
+
TResource & TIn,
|
|
218
|
+
[TOut, TResource & TIn]
|
|
219
|
+
>({
|
|
211
220
|
kind: "All",
|
|
212
|
-
actions: [action as Action, identity
|
|
221
|
+
actions: [action as Action, identity as Action],
|
|
213
222
|
});
|
|
214
223
|
|
|
215
224
|
// Step 3: all(extractIndex(0), chain(extractIndex(1), dispose)) → [TOut, unknown]
|
|
216
|
-
const disposeAndKeepResult = typedAction<
|
|
225
|
+
const disposeAndKeepResult = typedAction<
|
|
226
|
+
[TOut, TResource & TIn],
|
|
227
|
+
[TOut, unknown]
|
|
228
|
+
>({
|
|
217
229
|
kind: "All",
|
|
218
230
|
actions: [
|
|
219
231
|
extractIndex<[TOut, TResource & TIn], 0>(0) as Action,
|
|
@@ -257,9 +269,12 @@ export function augment<
|
|
|
257
269
|
kind: "Chain",
|
|
258
270
|
first: {
|
|
259
271
|
kind: "All",
|
|
260
|
-
actions: [action as Action, identity
|
|
272
|
+
actions: [action as Action, identity as Action],
|
|
273
|
+
},
|
|
274
|
+
rest: {
|
|
275
|
+
kind: "Invoke",
|
|
276
|
+
handler: { kind: "Builtin", builtin: { kind: "Merge" } },
|
|
261
277
|
},
|
|
262
|
-
rest: { kind: "Invoke", handler: { kind: "Builtin", builtin: { kind: "Merge" } } },
|
|
263
278
|
});
|
|
264
279
|
}
|
|
265
280
|
|
|
@@ -279,9 +294,10 @@ export function augment<
|
|
|
279
294
|
* Example:
|
|
280
295
|
* pipe(tap(pipe(pick("worktreePath", "description"), implement)), createPR)
|
|
281
296
|
*/
|
|
282
|
-
export function tap<
|
|
283
|
-
|
|
284
|
-
|
|
297
|
+
export function tap<
|
|
298
|
+
TInput extends Record<string, unknown>,
|
|
299
|
+
TRefs extends string = never,
|
|
300
|
+
>(action: Pipeable<TInput, any, TRefs>): TypedAction<TInput, TInput, TRefs> {
|
|
285
301
|
// Build AST directly — internal plumbing (action → constant → augment)
|
|
286
302
|
// can't go through typed chain/augment with invariant phantom fields.
|
|
287
303
|
// tap: all(chain(action, constant({})), identity()) → merge
|
|
@@ -293,12 +309,24 @@ export function tap<TInput extends Record<string, unknown>, TRefs extends string
|
|
|
293
309
|
{
|
|
294
310
|
kind: "Chain",
|
|
295
311
|
first: action as Action,
|
|
296
|
-
rest: {
|
|
312
|
+
rest: {
|
|
313
|
+
kind: "Invoke",
|
|
314
|
+
handler: {
|
|
315
|
+
kind: "Builtin",
|
|
316
|
+
builtin: { kind: "Constant", value: {} },
|
|
317
|
+
},
|
|
318
|
+
},
|
|
319
|
+
},
|
|
320
|
+
{
|
|
321
|
+
kind: "Invoke",
|
|
322
|
+
handler: { kind: "Builtin", builtin: { kind: "Identity" } },
|
|
297
323
|
},
|
|
298
|
-
{ kind: "Invoke", handler: { kind: "Builtin", builtin: { kind: "Identity" } } },
|
|
299
324
|
],
|
|
300
325
|
},
|
|
301
|
-
rest: {
|
|
326
|
+
rest: {
|
|
327
|
+
kind: "Invoke",
|
|
328
|
+
handler: { kind: "Builtin", builtin: { kind: "Merge" } },
|
|
329
|
+
},
|
|
302
330
|
});
|
|
303
331
|
}
|
|
304
332
|
|
|
@@ -306,10 +334,7 @@ export function tap<TInput extends Record<string, unknown>, TRefs extends string
|
|
|
306
334
|
// Range — produce an integer array [start, start+1, ..., end-1]
|
|
307
335
|
// ---------------------------------------------------------------------------
|
|
308
336
|
|
|
309
|
-
export function range(
|
|
310
|
-
start: number,
|
|
311
|
-
end: number,
|
|
312
|
-
): TypedAction<any, number[]> {
|
|
337
|
+
export function range(start: number, end: number): TypedAction<any, number[]> {
|
|
313
338
|
const result: number[] = [];
|
|
314
339
|
for (let i = start; i < end; i++) {
|
|
315
340
|
result.push(i);
|
|
@@ -325,11 +350,29 @@ export function range(
|
|
|
325
350
|
// ---------------------------------------------------------------------------
|
|
326
351
|
|
|
327
352
|
// Shared AST fragments for Option desugaring
|
|
328
|
-
const TAG_SOME: Action = {
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
const
|
|
353
|
+
const TAG_SOME: Action = {
|
|
354
|
+
kind: "Invoke",
|
|
355
|
+
handler: { kind: "Builtin", builtin: { kind: "Tag", value: "Some" } },
|
|
356
|
+
};
|
|
357
|
+
const TAG_NONE: Action = {
|
|
358
|
+
kind: "Invoke",
|
|
359
|
+
handler: { kind: "Builtin", builtin: { kind: "Tag", value: "None" } },
|
|
360
|
+
};
|
|
361
|
+
const EXTRACT_VALUE: Action = {
|
|
362
|
+
kind: "Invoke",
|
|
363
|
+
handler: {
|
|
364
|
+
kind: "Builtin",
|
|
365
|
+
builtin: { kind: "ExtractField", value: "value" },
|
|
366
|
+
},
|
|
367
|
+
};
|
|
368
|
+
const DROP: Action = {
|
|
369
|
+
kind: "Invoke",
|
|
370
|
+
handler: { kind: "Builtin", builtin: { kind: "Drop" } },
|
|
371
|
+
};
|
|
372
|
+
const IDENTITY: Action = {
|
|
373
|
+
kind: "Invoke",
|
|
374
|
+
handler: { kind: "Builtin", builtin: { kind: "Identity" } },
|
|
375
|
+
};
|
|
333
376
|
|
|
334
377
|
/** Wrap branch cases with ExtractField("value") auto-unwrapping. */
|
|
335
378
|
function optionBranch(someCaseBody: Action, noneCaseBody: Action): Action {
|
|
@@ -372,10 +415,12 @@ export const Option = {
|
|
|
372
415
|
* Desugars to: `branch({ Some: pipe(action, tag("Some")), None: tag("None") })`
|
|
373
416
|
*/
|
|
374
417
|
map<T, U>(action: Pipeable<T, U>): TypedAction<OptionT<T>, OptionT<U>> {
|
|
375
|
-
return typedAction(
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
418
|
+
return typedAction(
|
|
419
|
+
optionBranch(
|
|
420
|
+
{ kind: "Chain", first: action as Action, rest: TAG_SOME },
|
|
421
|
+
TAG_NONE,
|
|
422
|
+
),
|
|
423
|
+
);
|
|
379
424
|
},
|
|
380
425
|
|
|
381
426
|
/**
|
|
@@ -387,11 +432,10 @@ export const Option = {
|
|
|
387
432
|
*
|
|
388
433
|
* Desugars to: `branch({ Some: action, None: tag("None") })`
|
|
389
434
|
*/
|
|
390
|
-
andThen<T, U>(
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
));
|
|
435
|
+
andThen<T, U>(
|
|
436
|
+
action: Pipeable<T, OptionT<U>>,
|
|
437
|
+
): TypedAction<OptionT<T>, OptionT<U>> {
|
|
438
|
+
return typedAction(optionBranch(action as Action, TAG_NONE));
|
|
395
439
|
},
|
|
396
440
|
|
|
397
441
|
/**
|
|
@@ -412,7 +456,11 @@ export const Option = {
|
|
|
412
456
|
kind: "Branch",
|
|
413
457
|
cases: {
|
|
414
458
|
Some: { kind: "Chain", first: EXTRACT_VALUE, rest: IDENTITY },
|
|
415
|
-
None: {
|
|
459
|
+
None: {
|
|
460
|
+
kind: "Chain",
|
|
461
|
+
first: EXTRACT_VALUE,
|
|
462
|
+
rest: { kind: "Chain", first: DROP, rest: defaultAction as Action },
|
|
463
|
+
},
|
|
416
464
|
},
|
|
417
465
|
});
|
|
418
466
|
},
|
|
@@ -423,10 +471,7 @@ export const Option = {
|
|
|
423
471
|
* Desugars to: `branch({ Some: identity(), None: tag("None") })`
|
|
424
472
|
*/
|
|
425
473
|
flatten<T>(): TypedAction<OptionT<OptionT<T>>, OptionT<T>> {
|
|
426
|
-
return typedAction(optionBranch(
|
|
427
|
-
IDENTITY,
|
|
428
|
-
TAG_NONE,
|
|
429
|
-
));
|
|
474
|
+
return typedAction(optionBranch(IDENTITY, TAG_NONE));
|
|
430
475
|
},
|
|
431
476
|
|
|
432
477
|
/**
|
|
@@ -439,11 +484,10 @@ export const Option = {
|
|
|
439
484
|
*
|
|
440
485
|
* Desugars to: `branch({ Some: predicate, None: tag("None") })`
|
|
441
486
|
*/
|
|
442
|
-
filter<T>(
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
));
|
|
487
|
+
filter<T>(
|
|
488
|
+
predicate: Pipeable<T, OptionT<T>>,
|
|
489
|
+
): TypedAction<OptionT<T>, OptionT<T>> {
|
|
490
|
+
return typedAction(optionBranch(predicate as Action, TAG_NONE));
|
|
447
491
|
},
|
|
448
492
|
|
|
449
493
|
/**
|
|
@@ -469,12 +513,20 @@ export const Option = {
|
|
|
469
513
|
* Desugars to: `branch({ Some: pipe(drop(), constant(true)), None: pipe(drop(), constant(false)) })`
|
|
470
514
|
*/
|
|
471
515
|
isSome<T>(): TypedAction<OptionT<T>, boolean> {
|
|
472
|
-
const constTrue: Action = {
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
516
|
+
const constTrue: Action = {
|
|
517
|
+
kind: "Invoke",
|
|
518
|
+
handler: { kind: "Builtin", builtin: { kind: "Constant", value: true } },
|
|
519
|
+
};
|
|
520
|
+
const constFalse: Action = {
|
|
521
|
+
kind: "Invoke",
|
|
522
|
+
handler: { kind: "Builtin", builtin: { kind: "Constant", value: false } },
|
|
523
|
+
};
|
|
524
|
+
return typedAction(
|
|
525
|
+
optionBranch(
|
|
526
|
+
{ kind: "Chain", first: DROP, rest: constTrue },
|
|
527
|
+
{ kind: "Chain", first: DROP, rest: constFalse },
|
|
528
|
+
),
|
|
529
|
+
);
|
|
478
530
|
},
|
|
479
531
|
|
|
480
532
|
/**
|
|
@@ -485,12 +537,20 @@ export const Option = {
|
|
|
485
537
|
* Desugars to: `branch({ Some: pipe(drop(), constant(false)), None: pipe(drop(), constant(true)) })`
|
|
486
538
|
*/
|
|
487
539
|
isNone<T>(): TypedAction<OptionT<T>, boolean> {
|
|
488
|
-
const constTrue: Action = {
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
540
|
+
const constTrue: Action = {
|
|
541
|
+
kind: "Invoke",
|
|
542
|
+
handler: { kind: "Builtin", builtin: { kind: "Constant", value: true } },
|
|
543
|
+
};
|
|
544
|
+
const constFalse: Action = {
|
|
545
|
+
kind: "Invoke",
|
|
546
|
+
handler: { kind: "Builtin", builtin: { kind: "Constant", value: false } },
|
|
547
|
+
};
|
|
548
|
+
return typedAction(
|
|
549
|
+
optionBranch(
|
|
550
|
+
{ kind: "Chain", first: DROP, rest: constFalse },
|
|
551
|
+
{ kind: "Chain", first: DROP, rest: constTrue },
|
|
552
|
+
),
|
|
553
|
+
);
|
|
494
554
|
},
|
|
495
555
|
} as const;
|
|
496
556
|
|
|
@@ -499,8 +559,14 @@ export const Option = {
|
|
|
499
559
|
// ---------------------------------------------------------------------------
|
|
500
560
|
|
|
501
561
|
// Shared AST fragments for Result desugaring
|
|
502
|
-
const TAG_OK: Action = {
|
|
503
|
-
|
|
562
|
+
const TAG_OK: Action = {
|
|
563
|
+
kind: "Invoke",
|
|
564
|
+
handler: { kind: "Builtin", builtin: { kind: "Tag", value: "Ok" } },
|
|
565
|
+
};
|
|
566
|
+
const TAG_ERR: Action = {
|
|
567
|
+
kind: "Invoke",
|
|
568
|
+
handler: { kind: "Builtin", builtin: { kind: "Tag", value: "Err" } },
|
|
569
|
+
};
|
|
504
570
|
|
|
505
571
|
/** Wrap branch cases with ExtractField("value") auto-unwrapping. */
|
|
506
572
|
function resultBranch(okCaseBody: Action, errCaseBody: Action): Action {
|
|
@@ -540,10 +606,12 @@ export const Result = {
|
|
|
540
606
|
map<TValue, TOut, TError>(
|
|
541
607
|
action: Pipeable<TValue, TOut>,
|
|
542
608
|
): TypedAction<ResultT<TValue, TError>, ResultT<TOut, TError>> {
|
|
543
|
-
return typedAction(
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
609
|
+
return typedAction(
|
|
610
|
+
resultBranch(
|
|
611
|
+
{ kind: "Chain", first: action as Action, rest: TAG_OK },
|
|
612
|
+
TAG_ERR,
|
|
613
|
+
),
|
|
614
|
+
);
|
|
547
615
|
},
|
|
548
616
|
|
|
549
617
|
/**
|
|
@@ -554,10 +622,13 @@ export const Result = {
|
|
|
554
622
|
mapErr<TValue, TError, TErrorOut>(
|
|
555
623
|
action: Pipeable<TError, TErrorOut>,
|
|
556
624
|
): TypedAction<ResultT<TValue, TError>, ResultT<TValue, TErrorOut>> {
|
|
557
|
-
return typedAction(
|
|
558
|
-
TAG_OK,
|
|
559
|
-
|
|
560
|
-
|
|
625
|
+
return typedAction(
|
|
626
|
+
resultBranch(TAG_OK, {
|
|
627
|
+
kind: "Chain",
|
|
628
|
+
first: action as Action,
|
|
629
|
+
rest: TAG_ERR,
|
|
630
|
+
}),
|
|
631
|
+
);
|
|
561
632
|
},
|
|
562
633
|
|
|
563
634
|
/**
|
|
@@ -569,10 +640,7 @@ export const Result = {
|
|
|
569
640
|
andThen<TValue, TOut, TError>(
|
|
570
641
|
action: Pipeable<TValue, ResultT<TOut, TError>>,
|
|
571
642
|
): TypedAction<ResultT<TValue, TError>, ResultT<TOut, TError>> {
|
|
572
|
-
return typedAction(resultBranch(
|
|
573
|
-
action as Action,
|
|
574
|
-
TAG_ERR,
|
|
575
|
-
));
|
|
643
|
+
return typedAction(resultBranch(action as Action, TAG_ERR));
|
|
576
644
|
},
|
|
577
645
|
|
|
578
646
|
/**
|
|
@@ -584,10 +652,7 @@ export const Result = {
|
|
|
584
652
|
or<TValue, TError, TErrorOut>(
|
|
585
653
|
fallback: Pipeable<TError, ResultT<TValue, TErrorOut>>,
|
|
586
654
|
): TypedAction<ResultT<TValue, TError>, ResultT<TValue, TErrorOut>> {
|
|
587
|
-
return typedAction(resultBranch(
|
|
588
|
-
TAG_OK,
|
|
589
|
-
fallback as Action,
|
|
590
|
-
));
|
|
655
|
+
return typedAction(resultBranch(TAG_OK, fallback as Action));
|
|
591
656
|
},
|
|
592
657
|
|
|
593
658
|
/**
|
|
@@ -599,10 +664,12 @@ export const Result = {
|
|
|
599
664
|
and<TValue, TOut, TError>(
|
|
600
665
|
other: Pipeable<never, ResultT<TOut, TError>>,
|
|
601
666
|
): TypedAction<ResultT<TValue, TError>, ResultT<TOut, TError>> {
|
|
602
|
-
return typedAction(
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
667
|
+
return typedAction(
|
|
668
|
+
resultBranch(
|
|
669
|
+
{ kind: "Chain", first: DROP, rest: other as Action },
|
|
670
|
+
TAG_ERR,
|
|
671
|
+
),
|
|
672
|
+
);
|
|
606
673
|
},
|
|
607
674
|
|
|
608
675
|
/**
|
|
@@ -623,10 +690,7 @@ export const Result = {
|
|
|
623
690
|
__phantom_out?: () => TValue;
|
|
624
691
|
},
|
|
625
692
|
): TypedAction<ResultT<TValue, TError>, TValue> {
|
|
626
|
-
return typedAction(resultBranch(
|
|
627
|
-
IDENTITY,
|
|
628
|
-
defaultAction as Action,
|
|
629
|
-
));
|
|
693
|
+
return typedAction(resultBranch(IDENTITY, defaultAction as Action));
|
|
630
694
|
},
|
|
631
695
|
|
|
632
696
|
/**
|
|
@@ -634,11 +698,11 @@ export const Result = {
|
|
|
634
698
|
*
|
|
635
699
|
* Desugars to: `branch({ Ok: identity(), Err: tag("Err") })`
|
|
636
700
|
*/
|
|
637
|
-
flatten<TValue, TError>(): TypedAction<
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
));
|
|
701
|
+
flatten<TValue, TError>(): TypedAction<
|
|
702
|
+
ResultT<ResultT<TValue, TError>, TError>,
|
|
703
|
+
ResultT<TValue, TError>
|
|
704
|
+
> {
|
|
705
|
+
return typedAction(resultBranch(IDENTITY, TAG_ERR));
|
|
642
706
|
},
|
|
643
707
|
|
|
644
708
|
/**
|
|
@@ -646,11 +710,13 @@ export const Result = {
|
|
|
646
710
|
*
|
|
647
711
|
* Desugars to: `branch({ Ok: tag("Some"), Err: pipe(drop(), tag("None")) })`
|
|
648
712
|
*/
|
|
649
|
-
toOption<TValue, TError>(): TypedAction<
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
713
|
+
toOption<TValue, TError>(): TypedAction<
|
|
714
|
+
ResultT<TValue, TError>,
|
|
715
|
+
OptionT<TValue>
|
|
716
|
+
> {
|
|
717
|
+
return typedAction(
|
|
718
|
+
resultBranch(TAG_SOME, { kind: "Chain", first: DROP, rest: TAG_NONE }),
|
|
719
|
+
);
|
|
654
720
|
},
|
|
655
721
|
|
|
656
722
|
/**
|
|
@@ -658,53 +724,84 @@ export const Result = {
|
|
|
658
724
|
*
|
|
659
725
|
* Desugars to: `branch({ Ok: pipe(drop(), tag("None")), Err: tag("Some") })`
|
|
660
726
|
*/
|
|
661
|
-
toOptionErr<TValue, TError>(): TypedAction<
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
727
|
+
toOptionErr<TValue, TError>(): TypedAction<
|
|
728
|
+
ResultT<TValue, TError>,
|
|
729
|
+
OptionT<TError>
|
|
730
|
+
> {
|
|
731
|
+
return typedAction(
|
|
732
|
+
resultBranch({ kind: "Chain", first: DROP, rest: TAG_NONE }, TAG_SOME),
|
|
733
|
+
);
|
|
666
734
|
},
|
|
667
735
|
|
|
668
736
|
/**
|
|
669
737
|
* Swap Result/Option nesting.
|
|
670
738
|
* `Result<Option<TValue>, TError> → Option<Result<TValue, TError>>`
|
|
671
739
|
*/
|
|
672
|
-
transpose<TValue, TError>(): TypedAction<
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
740
|
+
transpose<TValue, TError>(): TypedAction<
|
|
741
|
+
ResultT<OptionT<TValue>, TError>,
|
|
742
|
+
OptionT<ResultT<TValue, TError>>
|
|
743
|
+
> {
|
|
744
|
+
return typedAction(
|
|
745
|
+
resultBranch(
|
|
746
|
+
// Ok case: receives Option<TValue>, branch on Some/None
|
|
747
|
+
{
|
|
748
|
+
kind: "Branch",
|
|
749
|
+
cases: {
|
|
750
|
+
Some: {
|
|
751
|
+
kind: "Chain",
|
|
752
|
+
first: EXTRACT_VALUE,
|
|
753
|
+
rest: { kind: "Chain", first: TAG_OK, rest: TAG_SOME },
|
|
754
|
+
},
|
|
755
|
+
None: {
|
|
756
|
+
kind: "Chain",
|
|
757
|
+
first: EXTRACT_VALUE,
|
|
758
|
+
rest: { kind: "Chain", first: DROP, rest: TAG_NONE },
|
|
759
|
+
},
|
|
760
|
+
},
|
|
680
761
|
},
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
)
|
|
762
|
+
// Err case: receives TError, wrap as Result.err then Option.some
|
|
763
|
+
{ kind: "Chain", first: TAG_ERR, rest: TAG_SOME },
|
|
764
|
+
),
|
|
765
|
+
);
|
|
685
766
|
},
|
|
686
767
|
|
|
687
768
|
/**
|
|
688
769
|
* Test if the value is Ok. `Result<TValue, TError> → boolean`
|
|
689
770
|
*/
|
|
690
771
|
isOk<TValue, TError>(): TypedAction<ResultT<TValue, TError>, boolean> {
|
|
691
|
-
const constTrue: Action = {
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
772
|
+
const constTrue: Action = {
|
|
773
|
+
kind: "Invoke",
|
|
774
|
+
handler: { kind: "Builtin", builtin: { kind: "Constant", value: true } },
|
|
775
|
+
};
|
|
776
|
+
const constFalse: Action = {
|
|
777
|
+
kind: "Invoke",
|
|
778
|
+
handler: { kind: "Builtin", builtin: { kind: "Constant", value: false } },
|
|
779
|
+
};
|
|
780
|
+
return typedAction(
|
|
781
|
+
resultBranch(
|
|
782
|
+
{ kind: "Chain", first: DROP, rest: constTrue },
|
|
783
|
+
{ kind: "Chain", first: DROP, rest: constFalse },
|
|
784
|
+
),
|
|
785
|
+
);
|
|
697
786
|
},
|
|
698
787
|
|
|
699
788
|
/**
|
|
700
789
|
* Test if the value is Err. `Result<TValue, TError> → boolean`
|
|
701
790
|
*/
|
|
702
791
|
isErr<TValue, TError>(): TypedAction<ResultT<TValue, TError>, boolean> {
|
|
703
|
-
const constTrue: Action = {
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
792
|
+
const constTrue: Action = {
|
|
793
|
+
kind: "Invoke",
|
|
794
|
+
handler: { kind: "Builtin", builtin: { kind: "Constant", value: true } },
|
|
795
|
+
};
|
|
796
|
+
const constFalse: Action = {
|
|
797
|
+
kind: "Invoke",
|
|
798
|
+
handler: { kind: "Builtin", builtin: { kind: "Constant", value: false } },
|
|
799
|
+
};
|
|
800
|
+
return typedAction(
|
|
801
|
+
resultBranch(
|
|
802
|
+
{ kind: "Chain", first: DROP, rest: constFalse },
|
|
803
|
+
{ kind: "Chain", first: DROP, rest: constTrue },
|
|
804
|
+
),
|
|
805
|
+
);
|
|
709
806
|
},
|
|
710
807
|
} as const;
|
package/src/chain.ts
CHANGED
|
@@ -1,8 +1,17 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
type Action,
|
|
3
|
+
type Pipeable,
|
|
4
|
+
type TypedAction,
|
|
5
|
+
typedAction,
|
|
6
|
+
} from "./ast.js";
|
|
2
7
|
|
|
3
8
|
export function chain<T1, T2, T3, R1 extends string, R2 extends string>(
|
|
4
9
|
first: Pipeable<T1, T2, R1>,
|
|
5
10
|
rest: Pipeable<T2, T3, R2>,
|
|
6
11
|
): TypedAction<T1, T3, R1 | R2> {
|
|
7
|
-
return typedAction({
|
|
12
|
+
return typedAction({
|
|
13
|
+
kind: "Chain",
|
|
14
|
+
first: first as Action,
|
|
15
|
+
rest: rest as Action,
|
|
16
|
+
});
|
|
8
17
|
}
|
package/src/effect-id.ts
CHANGED
|
@@ -2,14 +2,29 @@
|
|
|
2
2
|
// Shared effect ID counter for gensym'd effect identifiers
|
|
3
3
|
// ---------------------------------------------------------------------------
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
/** Branded ID for resume-style effect handlers. */
|
|
6
|
+
export type ResumeHandlerId = number & {
|
|
7
|
+
readonly __resumeHandlerBrand: unique symbol;
|
|
8
|
+
};
|
|
6
9
|
|
|
7
|
-
/**
|
|
8
|
-
export
|
|
9
|
-
|
|
10
|
+
/** Branded ID for restart-style effect handlers. */
|
|
11
|
+
export type RestartHandlerId = number & {
|
|
12
|
+
readonly __restartHandlerBrand: unique symbol;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
let nextId = 0;
|
|
16
|
+
|
|
17
|
+
/** Allocate a fresh, unique resume handler ID. */
|
|
18
|
+
export function allocateResumeHandlerId(): ResumeHandlerId {
|
|
19
|
+
return nextId++ as ResumeHandlerId;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** Allocate a fresh, unique restart handler ID. */
|
|
23
|
+
export function allocateRestartHandlerId(): RestartHandlerId {
|
|
24
|
+
return nextId++ as RestartHandlerId;
|
|
10
25
|
}
|
|
11
26
|
|
|
12
|
-
/** Reset the
|
|
27
|
+
/** Reset the ID counter. For test isolation only. */
|
|
13
28
|
export function resetEffectIdCounter(): void {
|
|
14
|
-
|
|
29
|
+
nextId = 0;
|
|
15
30
|
}
|