functionalscript 0.45.0 → 0.46.1
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/README.md +5 -3
- package/fjs/asn.1/module.f.mjs +8 -1
- package/fjs/asn.1/proof.f.d.mts +10 -0
- package/fjs/asn.1/proof.f.mjs +16 -0
- package/fjs/basen/base128/module.f.mjs +22 -5
- package/fjs/bnf/data/module.f.d.mts +32 -6
- package/fjs/bnf/data/module.f.mjs +112 -7
- package/fjs/bnf/data/proof.f.d.mts +2 -0
- package/fjs/bnf/data/proof.f.mjs +74 -3
- package/fjs/bnf/data/types.d.ts +20 -2
- package/fjs/bnf/descent/module.f.d.mts +28 -14
- package/fjs/bnf/descent/module.f.mjs +122 -77
- package/fjs/bnf/descent/proof.f.d.mts +2 -0
- package/fjs/bnf/descent/proof.f.mjs +117 -31
- package/fjs/bnf/descent/types.d.ts +12 -14
- package/fjs/bnf/ll1/module.f.d.mts +64 -22
- package/fjs/bnf/ll1/module.f.mjs +214 -154
- package/fjs/bnf/ll1/proof.f.d.mts +15 -2
- package/fjs/bnf/ll1/proof.f.mjs +323 -149
- package/fjs/bnf/ll1/types.d.ts +16 -24
- package/fjs/bnf/matcher/module.f.d.mts +66 -0
- package/fjs/bnf/matcher/module.f.mjs +81 -0
- package/fjs/bnf/matcher/proof.f.d.mts +10 -0
- package/fjs/bnf/matcher/proof.f.mjs +79 -0
- package/fjs/bnf/matcher/types.d.ts +54 -0
- package/fjs/bnf/testlib.f.d.mts +31 -0
- package/fjs/bnf/testlib.f.mjs +80 -0
- package/fjs/cas/cli/module.f.d.mts +1 -1
- package/fjs/cas/cli/module.f.mjs +14 -20
- package/fjs/cas/cli/proof.f.d.mts +1 -3
- package/fjs/cas/cli/proof.f.mjs +44 -33
- package/fjs/cas/evo/module.f.d.mts +64 -18
- package/fjs/cas/evo/module.f.mjs +148 -70
- package/fjs/cas/evo/proof.f.d.mts +10 -1
- package/fjs/cas/evo/proof.f.mjs +305 -223
- package/fjs/cas/evo/types.d.ts +45 -9
- package/fjs/cas/module.f.d.mts +18 -32
- package/fjs/cas/module.f.mjs +129 -128
- package/fjs/cas/proof.f.d.mts +11 -8
- package/fjs/cas/proof.f.mjs +259 -147
- package/fjs/cas/types.d.ts +24 -10
- package/fjs/ci/config/module.f.d.mts +2 -2
- package/fjs/ci/config/module.f.mjs +2 -2
- package/fjs/ci/module.f.d.mts +5 -5
- package/fjs/ci/module.f.mjs +8 -7
- package/fjs/ci/nix/module.f.d.mts +7 -5
- package/fjs/ci/nix/module.f.mjs +13 -12
- package/fjs/ci/nix/proof.f.mjs +2 -2
- package/fjs/ci/proof.f.mjs +9 -5
- package/fjs/cli/module.f.d.mts +4 -6
- package/fjs/cli/module.f.mjs +4 -8
- package/fjs/cli/proof.f.mjs +17 -16
- package/fjs/cli/types.d.ts +2 -3
- package/fjs/common/monoid/types.d.ts +1 -1
- package/fjs/crypto/hmac/module.f.mjs +2 -2
- package/fjs/crypto/sha2/module.f.mjs +3 -1
- package/fjs/crypto/sha2/proof.f.d.mts +1 -0
- package/fjs/crypto/sha2/proof.f.mjs +24 -0
- package/fjs/crypto/sha2/types.d.ts +11 -0
- package/fjs/crypto/sign/module.f.mjs +2 -2
- package/fjs/dev/module.f.d.mts +13 -4
- package/fjs/dev/module.f.mjs +56 -27
- package/fjs/dev/update/module.f.d.mts +9 -4
- package/fjs/dev/update/module.f.mjs +14 -10
- package/fjs/dev/update/proof.f.d.mts +1 -3
- package/fjs/dev/update/proof.f.mjs +10 -5
- package/fjs/djs/module.f.d.mts +13 -5
- package/fjs/djs/module.f.mjs +31 -16
- package/fjs/djs/parser/module.f.d.mts +13 -3
- package/fjs/djs/parser/module.f.mjs +117 -16
- package/fjs/djs/parser/proof.f.d.mts +5 -1
- package/fjs/djs/parser/proof.f.mjs +274 -12
- package/fjs/djs/parser/types.d.ts +7 -1
- package/fjs/djs/proof.f.d.mts +18 -2
- package/fjs/djs/proof.f.mjs +187 -12
- package/fjs/djs/serializer/module.f.d.mts +25 -8
- package/fjs/djs/serializer/module.f.mjs +61 -15
- package/fjs/djs/serializer/proof.f.d.mts +5 -0
- package/fjs/djs/serializer/proof.f.mjs +23 -1
- package/fjs/djs/tokenizer/module.f.d.mts +17 -5
- package/fjs/djs/tokenizer/module.f.mjs +99 -60
- package/fjs/djs/tokenizer/proof.f.d.mts +1 -1
- package/fjs/djs/tokenizer/proof.f.mjs +74 -67
- package/fjs/djs/transpiler/module.f.d.mts +12 -7
- package/fjs/djs/transpiler/module.f.mjs +82 -56
- package/fjs/djs/transpiler/types.d.ts +7 -3
- package/fjs/djs/types.d.ts +7 -1
- package/fjs/effects/list/module.f.d.mts +14 -11
- package/fjs/effects/list/module.f.mjs +13 -11
- package/fjs/effects/list/types.d.ts +27 -7
- package/fjs/effects/memory/module.f.d.mts +2 -1
- package/fjs/effects/memory/module.f.mjs +3 -4
- package/fjs/effects/memory/proof.f.mjs +13 -7
- package/fjs/effects/memory/types.d.ts +4 -3
- package/fjs/effects/mock/module.f.d.mts +20 -4
- package/fjs/effects/mock/module.f.mjs +37 -5
- package/fjs/effects/mock/types.d.ts +10 -1
- package/fjs/effects/module.d.mts +4 -2
- package/fjs/effects/module.f.d.mts +466 -280
- package/fjs/effects/module.f.mjs +537 -299
- package/fjs/effects/module.mjs +2 -1
- package/fjs/effects/node/memory/module.d.mts +4 -2
- package/fjs/effects/node/memory/module.mjs +6 -3
- package/fjs/effects/node/memory/proof.mjs +10 -4
- package/fjs/effects/node/module.d.mts +4 -3
- package/fjs/effects/node/module.f.d.mts +168 -33
- package/fjs/effects/node/module.f.mjs +256 -52
- package/fjs/effects/node/module.mjs +57 -34
- package/fjs/effects/node/proof.f.d.mts +28 -2
- package/fjs/effects/node/proof.f.mjs +161 -42
- package/fjs/effects/node/types.d.ts +106 -18
- package/fjs/effects/node/virtual/module.f.d.mts +18 -4
- package/fjs/effects/node/virtual/module.f.mjs +110 -68
- package/fjs/effects/node/virtual/proof.f.d.mts +28 -2
- package/fjs/effects/node/virtual/proof.f.mjs +190 -9
- package/fjs/effects/proof.f.d.mts +69 -37
- package/fjs/effects/proof.f.mjs +410 -130
- package/fjs/effects/types.d.ts +161 -33
- package/fjs/emergent_testing/module.f.d.mts +19 -12
- package/fjs/emergent_testing/module.f.mjs +93 -33
- package/fjs/emergent_testing/proof.f.d.mts +21 -7
- package/fjs/emergent_testing/proof.f.mjs +166 -32
- package/fjs/emergent_testing/types.d.ts +22 -4
- package/fjs/fsm/module.f.d.mts +14 -4
- package/fjs/fsm/module.f.mjs +54 -37
- package/fjs/fsm/proof.f.d.mts +2 -0
- package/fjs/fsm/proof.f.mjs +83 -114
- package/fjs/js/keywords/module.f.d.mts +52 -0
- package/fjs/js/keywords/module.f.mjs +72 -0
- package/fjs/js/keywords/proof.f.d.mts +3 -0
- package/fjs/js/keywords/proof.f.mjs +13 -0
- package/fjs/js/tokenizer/module.f.d.mts +26 -6
- package/fjs/js/tokenizer/module.f.mjs +145 -159
- package/fjs/js/tokenizer/proof.f.d.mts +1 -0
- package/fjs/js/tokenizer/proof.f.mjs +54 -24
- package/fjs/js/tokenizer/types.d.ts +33 -24
- package/fjs/mcp/cas/module.f.d.mts +1 -6
- package/fjs/mcp/cas/module.f.mjs +55 -51
- package/fjs/mcp/cas/proof.f.d.mts +15 -0
- package/fjs/mcp/cas/proof.f.mjs +174 -0
- package/fjs/mcp/evo/module.f.d.mts +19 -10
- package/fjs/mcp/evo/module.f.mjs +48 -27
- package/fjs/mcp/evo/proof.f.d.mts +6 -1
- package/fjs/mcp/evo/proof.f.mjs +115 -31
- package/fjs/mcp/module.f.d.mts +4 -4
- package/fjs/mcp/module.f.mjs +6 -6
- package/fjs/mcp/proof.f.d.mts +5 -3
- package/fjs/mcp/proof.f.mjs +112 -46
- package/fjs/media/html/module.f.mjs +1 -1
- package/fjs/media/json/extended/module.f.d.mts +82 -0
- package/fjs/media/json/extended/module.f.mjs +153 -0
- package/fjs/media/json/extended/proof.f.d.mts +42 -0
- package/fjs/media/json/extended/proof.f.mjs +127 -0
- package/fjs/media/json/extended/types.d.ts +23 -0
- package/fjs/media/json/module.f.d.mts +8 -2
- package/fjs/media/json/module.f.mjs +43 -41
- package/fjs/media/json/number/module.f.d.mts +59 -0
- package/fjs/media/json/number/module.f.mjs +136 -0
- package/fjs/media/json/number/proof.f.d.mts +24 -0
- package/fjs/media/json/number/proof.f.mjs +86 -0
- package/fjs/media/json/number/types.d.ts +28 -0
- package/fjs/media/json/parser/module.f.d.mts +25 -13
- package/fjs/media/json/parser/module.f.mjs +114 -70
- package/fjs/media/json/parser/proof.f.d.mts +5 -0
- package/fjs/media/json/parser/proof.f.mjs +31 -1
- package/fjs/media/json/parser/types.d.ts +33 -14
- package/fjs/media/json/rtti/module.f.d.mts +1 -1
- package/fjs/media/json/rtti/module.f.mjs +1 -1
- package/fjs/media/json/rtti/proof.f.mjs +9 -9
- package/fjs/media/json/schema/module.f.mjs +3 -13
- package/fjs/media/json/schema/proof.f.d.mts +0 -1
- package/fjs/media/json/schema/proof.f.mjs +1 -2
- package/fjs/media/json/serializer/module.f.d.mts +32 -1
- package/fjs/media/json/serializer/module.f.mjs +64 -2
- package/fjs/media/json/tokenizer/module.f.mjs +7 -3
- package/fjs/media/json/tokenizer/proof.f.d.mts +6 -0
- package/fjs/media/json/tokenizer/proof.f.mjs +62 -21
- package/fjs/media/json/types.d.ts +36 -10
- package/fjs/media/lock/module.f.d.mts +100 -0
- package/fjs/media/lock/module.f.mjs +125 -0
- package/fjs/media/lock/proof.f.d.mts +33 -0
- package/fjs/media/lock/proof.f.mjs +196 -0
- package/fjs/media/lock/types.d.ts +15 -0
- package/fjs/media/module.f.d.mts +6 -5
- package/fjs/media/module.f.mjs +8 -7
- package/fjs/media/note/module.f.d.mts +121 -0
- package/fjs/media/note/module.f.mjs +131 -0
- package/fjs/media/note/proof.f.d.mts +29 -0
- package/fjs/media/note/proof.f.mjs +150 -0
- package/fjs/media/note/types.d.ts +10 -0
- package/fjs/media/proof.f.d.mts +4 -1
- package/fjs/media/proof.f.mjs +40 -21
- package/fjs/media/revision/module.f.d.mts +78 -7
- package/fjs/media/revision/module.f.mjs +119 -12
- package/fjs/media/revision/proof.f.d.mts +10 -0
- package/fjs/media/revision/proof.f.mjs +88 -0
- package/fjs/media/revision/types.d.ts +34 -5
- package/fjs/media/type/module.f.d.mts +33 -15
- package/fjs/media/type/module.f.mjs +35 -29
- package/fjs/media/type/proof.f.d.mts +2 -1
- package/fjs/media/type/proof.f.mjs +30 -10
- package/fjs/module.f.mjs +29 -8
- package/fjs/nanvm/proof.f.mjs +3 -3
- package/fjs/nanvm/rust/module.f.mjs +1 -1
- package/fjs/nanvm/update/module.f.d.mts +4 -4
- package/fjs/nanvm/update/module.f.mjs +8 -9
- package/fjs/nanvm/update/proof.f.mjs +4 -3
- package/fjs/proof.f.d.mts +3 -3
- package/fjs/proof.f.mjs +36 -10
- package/fjs/protocol/json_rpc/module.f.d.mts +2 -2
- package/fjs/protocol/json_rpc/module.f.mjs +3 -3
- package/fjs/protocol/json_rpc/proof.f.mjs +4 -4
- package/fjs/protocol/mcp/module.f.d.mts +10 -13
- package/fjs/protocol/mcp/module.f.mjs +74 -58
- package/fjs/protocol/mcp/proof.f.d.mts +13 -4
- package/fjs/protocol/mcp/proof.f.mjs +193 -61
- package/fjs/protocol/mcp/stdio/module.f.d.mts +15 -7
- package/fjs/protocol/mcp/stdio/module.f.mjs +38 -25
- package/fjs/protocol/mcp/stdio/proof.f.d.mts +4 -2
- package/fjs/protocol/mcp/stdio/proof.f.mjs +45 -12
- package/fjs/protocol/mcp/stdio/types.d.ts +1 -1
- package/fjs/protocol/mcp/types.d.ts +17 -6
- package/fjs/sul/id/module.f.d.mts +0 -1
- package/fjs/sul/id/module.f.mjs +2 -3
- package/fjs/sul/level/hash/module.f.mjs +2 -1
- package/fjs/sul/level/hash/proof.f.mjs +2 -2
- package/fjs/sul/module.f.mjs +18 -13
- package/fjs/text/code_point/module.f.d.mts +8 -0
- package/fjs/text/code_point/module.f.mjs +8 -1
- package/fjs/text/code_point/proof.f.d.mts +1 -0
- package/fjs/text/code_point/proof.f.mjs +11 -0
- package/fjs/text/sgr/module.f.d.mts +4 -6
- package/fjs/text/sgr/module.f.mjs +6 -7
- package/fjs/text/utf16/module.f.mjs +7 -2
- package/fjs/text/utf16/proof.f.mjs +3 -3
- package/fjs/text/utf8/module.f.mjs +4 -2
- package/fjs/types/array/module.f.mjs +14 -2
- package/fjs/types/bit_vec/module.f.d.mts +0 -2
- package/fjs/types/bit_vec/module.f.mjs +46 -32
- package/fjs/types/bit_vec/proof.f.mjs +2 -2
- package/fjs/types/btree/remove/module.f.mjs +1 -1
- package/fjs/types/btree/set/module.f.mjs +9 -11
- package/fjs/types/btree/set/proof.f.mjs +12 -0
- package/fjs/types/byte_set/module.f.d.mts +11 -4
- package/fjs/types/byte_set/module.f.mjs +14 -6
- package/fjs/types/byte_set/proof.f.mjs +7 -7
- package/fjs/types/function/compare/module.f.mjs +10 -3
- package/fjs/types/list/module.f.d.mts +1 -1
- package/fjs/types/list/module.f.mjs +1 -1
- package/fjs/types/nullable/module.f.d.mts +18 -4
- package/fjs/types/nullable/module.f.mjs +21 -4
- package/fjs/types/nullable/proof.f.d.mts +4 -0
- package/fjs/types/nullable/proof.f.mjs +15 -0
- package/fjs/types/object/module.f.d.mts +12 -2
- package/fjs/types/object/module.f.mjs +11 -1
- package/fjs/types/patricia_trie/module.f.mjs +26 -11
- package/fjs/types/result/module.f.d.mts +3 -3
- package/fjs/types/result/module.f.mjs +3 -3
- package/fjs/types/rtti/common/module.f.d.mts +25 -28
- package/fjs/types/rtti/common/module.f.mjs +36 -32
- package/fjs/types/rtti/common/proof.f.mjs +5 -5
- package/fjs/types/rtti/data/module.f.d.mts +14 -0
- package/fjs/types/rtti/data/module.f.mjs +25 -2
- package/fjs/types/rtti/data/proof.f.d.mts +1 -0
- package/fjs/types/rtti/data/proof.f.mjs +25 -2
- package/fjs/types/rtti/parse/module.f.d.mts +24 -14
- package/fjs/types/rtti/parse/module.f.mjs +37 -28
- package/fjs/types/rtti/parse/proof.f.d.mts +3 -2
- package/fjs/types/rtti/parse/proof.f.mjs +33 -14
- package/fjs/types/rtti/proof.f.mjs +3 -1
- package/fjs/types/rtti/ts/module.f.mjs +13 -17
- package/fjs/types/rtti/ts/types.d.ts +14 -1
- package/fjs/types/rtti/validate/module.f.d.mts +81 -19
- package/fjs/types/rtti/validate/module.f.mjs +107 -61
- package/fjs/types/rtti/validate/proof.f.d.mts +13 -10
- package/fjs/types/rtti/validate/proof.f.mjs +186 -197
- package/fjs/types/sorted_set/module.f.d.mts +18 -0
- package/fjs/types/sorted_set/module.f.mjs +22 -0
- package/fjs/types/sorted_set/proof.f.d.mts +1 -0
- package/fjs/types/sorted_set/proof.f.mjs +16 -1
- package/fjs/types/uint8array/module.f.d.mts +1 -1
- package/fjs/types/uint8array/module.f.mjs +1 -1
- package/fjs/website/module.f.d.mts +3 -3
- package/fjs/website/module.f.mjs +4 -7
- package/fjs/website/proof.f.mjs +2 -1
- package/package.json +2 -2
- package/fjs/dev/package_json/module.f.d.mts +0 -39
- package/fjs/dev/package_json/module.f.mjs +0 -40
- package/fjs/dev/package_json/proof.f.d.mts +0 -6
- package/fjs/dev/package_json/proof.f.mjs +0 -32
- package/fjs/effects/eff/module.f.d.mts +0 -20
- package/fjs/effects/eff/module.f.mjs +0 -70
- package/fjs/effects/eff/proof.f.d.mts +0 -15
- package/fjs/effects/eff/proof.f.mjs +0 -69
- package/fjs/effects/eff/types.d.ts +0 -71
- package/fjs/types/rtti/validate/types.d.ts +0 -6
package/fjs/effects/proof.f.mjs
CHANGED
|
@@ -1,35 +1,49 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @import { Effect,
|
|
2
|
+
* @import { Effect, Func, Operation } from './types.ts'
|
|
3
|
+
* @import { Result } from '../types/result/types.ts'
|
|
3
4
|
*/
|
|
4
5
|
|
|
5
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
catchStep, do_, foldStep, forEachStep, history, historyStep, mapStep, match,
|
|
8
|
+
partialMatch, pure, pureError, pureOk, resultMapStep, resultStep, runPure, step,
|
|
9
|
+
unwrapStep,
|
|
10
|
+
} from './module.f.mjs'
|
|
6
11
|
import { error, ok } from '../types/result/module.f.mjs'
|
|
7
|
-
import { assert, assertEq } from '../asserts/module.f.mjs'
|
|
12
|
+
import { assert, assertEq, todo } from '../asserts/module.f.mjs'
|
|
8
13
|
|
|
9
14
|
/**
|
|
10
|
-
* Asserts that `e`
|
|
11
|
-
* `eff/proof.f.ts` shares this definition instead of repeating it.
|
|
15
|
+
* Asserts that `e` reaches `expected` without performing a command.
|
|
12
16
|
*
|
|
13
17
|
* Not `assertEq(runPure(e), [expected])`: `assertEq` compares with `===`, so a
|
|
14
|
-
* freshly allocated `
|
|
15
|
-
* the option's shape first, then compare the value inside it.
|
|
18
|
+
* freshly allocated option or `Result` is never equal to the returned one.
|
|
19
|
+
* Assert the option's shape first, then compare the value inside it.
|
|
20
|
+
*
|
|
16
21
|
* @template {Operation} O
|
|
17
22
|
* @template T
|
|
18
|
-
* @
|
|
19
|
-
* @param {T}
|
|
23
|
+
* @template E
|
|
24
|
+
* @param {Effect<O, T, E>} e
|
|
25
|
+
* @param {Result<T, E>} expected
|
|
20
26
|
*/
|
|
21
|
-
|
|
27
|
+
const assertPure = (e, expected) => {
|
|
22
28
|
const o = runPure(e)
|
|
23
29
|
assert(o.length === 1, e)
|
|
24
|
-
assertEq(o[0], expected)
|
|
30
|
+
assertEq(o[0][0], expected[0])
|
|
31
|
+
assertEq(o[0][1], expected[1])
|
|
25
32
|
}
|
|
26
33
|
|
|
27
|
-
/**
|
|
34
|
+
/**
|
|
35
|
+
* `Operation` requires a `Result` return, so a runner always has somewhere to
|
|
36
|
+
* answer `error(notImplemented)` — and that requirement is what lets an effect
|
|
37
|
+
* carry its error channel in the type rather than inside an opaque payload.
|
|
38
|
+
* @typedef {readonly['add', (a: number, b: number) => Result<number, string>]} _AddOp
|
|
39
|
+
*/
|
|
28
40
|
|
|
29
|
-
/** @type {(command: 'add') => (a: number, b: number) => Effect<_AddOp, number>} */
|
|
41
|
+
/** @type {(command: 'add') => (a: number, b: number) => Effect<_AddOp, number, string>} */
|
|
30
42
|
const doAdd = do_
|
|
31
43
|
|
|
32
|
-
const next = match(
|
|
44
|
+
const next = match({
|
|
45
|
+
add: (/** @type {number} */ a, /** @type {number} */ b) => ok(a + b),
|
|
46
|
+
})
|
|
33
47
|
|
|
34
48
|
/**
|
|
35
49
|
* An operation set whose command is any `string`, which is what a `Do` node
|
|
@@ -37,72 +51,143 @@ const next = match(/** @type {OperationMap<_AddOp, number>} */ ({ add: (a, b) =>
|
|
|
37
51
|
* nothing stops it naming a member `map` inherits from `Object.prototype`
|
|
38
52
|
* rather than an own handler. `match` must refuse those, and this type is how a
|
|
39
53
|
* proof says so without an `as` cast.
|
|
40
|
-
* @typedef {readonly[string, (a: number) => number]} _AnyOp
|
|
54
|
+
* @typedef {readonly[string, (a: number) => Result<number, string>]} _AnyOp
|
|
41
55
|
*/
|
|
42
56
|
|
|
43
|
-
/** @type {(command: string) => (a: number) => Effect<_AnyOp, number>} */
|
|
57
|
+
/** @type {(command: string) => (a: number) => Effect<_AnyOp, number, string>} */
|
|
44
58
|
const doAny = do_
|
|
45
59
|
|
|
46
|
-
const anyNext = match(/** @type {
|
|
60
|
+
const anyNext = match({ add: (/** @type {number} */ a) => ok(a + 1) })
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* The same map, reached through the runner that may decline a command. The
|
|
64
|
+
* handler's return is annotated so it and `onMissing` agree on one `R` — a
|
|
65
|
+
* runner's answer type is shared by both, which is the whole reason
|
|
66
|
+
* `partialMatch` takes the injector from its caller.
|
|
67
|
+
*/
|
|
68
|
+
const anyPartial = partialMatch(
|
|
69
|
+
/** @type {readonly string[]} */ (['add', 'sub']),
|
|
70
|
+
(/** @type {string} */ command) =>
|
|
71
|
+
/** @type {Result<number, string>} */ (error(`declined: ${command}`)),
|
|
72
|
+
)({
|
|
73
|
+
add: (/** @type {number} */ a) => /** @type {Result<number, string>} */ (ok(a + 1)),
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
// ── Composition ──────────────────────────────────────────────────────────────
|
|
77
|
+
/**
|
|
78
|
+
* A fallible operation, spelled the way every operation is spelled: the
|
|
79
|
+
* `Result` is in the command's declared return type, so `do_` already builds an
|
|
80
|
+
* `Effect` and the runner's handler already answers with `ok` / `error`.
|
|
81
|
+
* @typedef {readonly['div', (a: number, b: number) => Result<number, string>]} _DivOp
|
|
82
|
+
*/
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* A second operation, so a chain can join two of them and the operation sets
|
|
86
|
+
* union.
|
|
87
|
+
* @typedef {readonly['neg', (a: number) => Result<number, string>]} _NegOp
|
|
88
|
+
*/
|
|
89
|
+
|
|
90
|
+
/** @typedef {_DivOp | _NegOp} _Op */
|
|
91
|
+
|
|
92
|
+
/** @type {Func<_DivOp>} */
|
|
93
|
+
const div = do_('div')
|
|
94
|
+
|
|
95
|
+
/** @type {Func<_NegOp>} */
|
|
96
|
+
const neg = do_('neg')
|
|
97
|
+
|
|
98
|
+
const nextArith = match({
|
|
99
|
+
div: (/** @type {number} */ a, /** @type {number} */ b) =>
|
|
100
|
+
b === 0 ? error('div by zero') : ok(a / b),
|
|
101
|
+
neg: (/** @type {number} */ a) => ok(-a),
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Runs an effect to completion against the two operations above — `asyncRun`'s
|
|
106
|
+
* loop without the `await`, which is all a synchronous runner is.
|
|
107
|
+
* @type {<T, E>(e: Effect<_Op, T, E>) => Result<T, E>}
|
|
108
|
+
*/
|
|
109
|
+
const run = e => {
|
|
110
|
+
let current = e
|
|
111
|
+
while (true) {
|
|
112
|
+
const r = nextArith(current)
|
|
113
|
+
if (r[0] === 'done') { return r[1] }
|
|
114
|
+
current = r[2](r[1])
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* The `Result` an effect reaches without performing a command.
|
|
120
|
+
* @type {<O extends Operation, T, E>(e: Effect<O, T, E>) => Result<T, E>}
|
|
121
|
+
*/
|
|
122
|
+
const pureResult = e => {
|
|
123
|
+
const o = runPure(e)
|
|
124
|
+
assert(o.length === 1, e)
|
|
125
|
+
return o[0]
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* The `ok` value an effect reaches without performing a command.
|
|
130
|
+
* @type {<O extends Operation, T, E>(e: Effect<O, T, E>) => T}
|
|
131
|
+
*/
|
|
132
|
+
const unwrapPure = e => {
|
|
133
|
+
const r = pureResult(e)
|
|
134
|
+
assert(r[0] === 'ok', r)
|
|
135
|
+
return r[1]
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/** @type {<T, E>(r: Result<T, E>, expected: T) => void} */
|
|
139
|
+
const assertOk = (r, expected) => {
|
|
140
|
+
assert(r[0] === 'ok', r)
|
|
141
|
+
assertEq(r[1], expected)
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/** @type {<T, E>(r: Result<T, E>, expected: E) => void} */
|
|
145
|
+
const assertError = (r, expected) => {
|
|
146
|
+
assert(r[0] === 'error', r)
|
|
147
|
+
assertEq(r[1], expected)
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/** @type {(v: number) => Effect<never, number, string>} */
|
|
151
|
+
const positive = v => v > 0 ? pureOk(v) : pureError('not positive')
|
|
152
|
+
|
|
153
|
+
/** @type {(v: number) => Effect<never, number, number>} */
|
|
154
|
+
const small = v => v < 10 ? pureOk(v) : pureError(v)
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Two adjacent links failing in different ways. The annotation is the claim:
|
|
158
|
+
* the chain's error channel is the **union** of theirs, so neither link had to
|
|
159
|
+
* be pre-widened to the other's error type.
|
|
160
|
+
* @type {(v: number) => Effect<never, number, string | number>}
|
|
161
|
+
*/
|
|
162
|
+
const checked = v => {
|
|
163
|
+
const x0 = step(pureOk(v), positive)
|
|
164
|
+
return step(x0, small)
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* The anything-accepting renderer {@link unwrapStep}'s doc calls an escape
|
|
169
|
+
* hatch. It is the honest choice *here* — these proofs are about `unwrapStep`
|
|
170
|
+
* itself, not about any particular channel — and the wrong one almost anywhere
|
|
171
|
+
* else, since accepting every error is what stops a widened channel from being
|
|
172
|
+
* a compile error.
|
|
173
|
+
*
|
|
174
|
+
* @type {(e: unknown) => string}
|
|
175
|
+
*/
|
|
176
|
+
const show = e => `${e}`
|
|
47
177
|
|
|
48
178
|
export const proof = {
|
|
49
|
-
|
|
50
|
-
empty: () => {
|
|
51
|
-
const e = foldStep(pure(/** @type {readonly number[]} */ ([])), 10, x => s => pure(s + x))
|
|
52
|
-
assertPure(e, 10)
|
|
53
|
-
},
|
|
54
|
-
threadsState: () => {
|
|
55
|
-
const e = foldStep(pure([1, 2, 3, 4]), 0, x => s => pure(s + x))
|
|
56
|
-
assertPure(e, 10)
|
|
57
|
-
},
|
|
58
|
-
order: () => {
|
|
59
|
-
const e = foldStep(pure(['a', 'b', 'c']), '', x => s => pure(s + x))
|
|
60
|
-
assertPure(e, 'abc')
|
|
61
|
-
},
|
|
62
|
-
},
|
|
63
|
-
forEachStep: {
|
|
64
|
-
empty: () => {
|
|
65
|
-
const e = forEachStep(pure(/** @type {readonly number[]} */ ([])), () => pure(undefined))
|
|
66
|
-
assertPure(e, undefined)
|
|
67
|
-
},
|
|
68
|
-
runs: () => {
|
|
69
|
-
const e = forEachStep(pure([1, 2, 3]), () => pure(undefined))
|
|
70
|
-
assertPure(e, undefined)
|
|
71
|
-
},
|
|
72
|
-
},
|
|
73
|
-
okStep: {
|
|
179
|
+
runPure: {
|
|
74
180
|
ok: () => {
|
|
75
|
-
|
|
76
|
-
const o = runPure(e)
|
|
77
|
-
assert(o.length === 1, e)
|
|
78
|
-
const [r] = o
|
|
79
|
-
assert(r[0] === 'ok', r)
|
|
80
|
-
assertEq(r[1], 10)
|
|
181
|
+
assertPure(pure(ok(5)), ok(5))
|
|
81
182
|
},
|
|
82
183
|
error: () => {
|
|
83
|
-
|
|
84
|
-
pure(error(/** @type {string} */ ('oops'))),
|
|
85
|
-
okStep(/** @type {(value: number) => Effect<never, import('../types/result/types.ts').Result<number, string>>} */
|
|
86
|
-
(v => pure(ok(v * 2)))))
|
|
87
|
-
const o = runPure(e)
|
|
88
|
-
assert(o.length === 1, e)
|
|
89
|
-
const [r] = o
|
|
90
|
-
assert(r[0] === 'error', r)
|
|
91
|
-
assertEq(r[1], 'oops')
|
|
92
|
-
},
|
|
93
|
-
},
|
|
94
|
-
runPure: {
|
|
95
|
-
pure: () => {
|
|
96
|
-
const o = runPure(pure(5))
|
|
97
|
-
assert(o.length === 1, o)
|
|
98
|
-
assertEq(o[0], 5)
|
|
184
|
+
assertPure(pure(error('boom')), error('boom'))
|
|
99
185
|
},
|
|
100
|
-
// A pure `null` is `[null]`, never `[]`. Collapsing the two is what
|
|
101
|
-
// `T | null` result would do, and why the option is tagged
|
|
186
|
+
// A pure `null` is `[ok(null)]`, never `[]`. Collapsing the two is what
|
|
187
|
+
// a bare `T | null` result would do, and why the option is tagged
|
|
188
|
+
// separately from the `Result` inside it.
|
|
102
189
|
pureNull: () => {
|
|
103
|
-
|
|
104
|
-
assertEq(o.length, 1, o)
|
|
105
|
-
assertEq(o[0], null)
|
|
190
|
+
assertPure(pure(ok(null)), ok(null))
|
|
106
191
|
},
|
|
107
192
|
do_: () => {
|
|
108
193
|
assertEq(runPure(doAdd('add')(2, 3)).length, 0)
|
|
@@ -110,8 +195,8 @@ export const proof = {
|
|
|
110
195
|
},
|
|
111
196
|
// The one place a `Do` node is opened on purpose: `do_` builds the command,
|
|
112
197
|
// the payload it was called with, and a continuation that resumes with the
|
|
113
|
-
// command's output. Everything else goes through `
|
|
114
|
-
// `runPure`.
|
|
198
|
+
// command's output. Everything else goes through `match`, `partialMatch`,
|
|
199
|
+
// or `runPure`.
|
|
115
200
|
doNode: () => {
|
|
116
201
|
const e = doAdd('add')(2, 3)
|
|
117
202
|
assert(typeof e !== 'function', e)
|
|
@@ -120,21 +205,34 @@ export const proof = {
|
|
|
120
205
|
const [a, b] = payload
|
|
121
206
|
assertEq(a, 2, payload)
|
|
122
207
|
assertEq(b, 3, payload)
|
|
123
|
-
|
|
208
|
+
// The command's output is the whole `Result` — `Operation` requires one
|
|
209
|
+
// so a runner always has somewhere to answer a refusal — so this
|
|
210
|
+
// resumes with `ok(5)` rather than a bare `5`.
|
|
211
|
+
assertPure(continuation(ok(5)), ok(5))
|
|
212
|
+
},
|
|
213
|
+
// A runner hands a failed command back through the *ordinary* continuation,
|
|
214
|
+
// which is what makes `error(notImplemented)` something a program can
|
|
215
|
+
// recover from. Pinned here because it is the property the whole error
|
|
216
|
+
// channel rests on: had the representation short-circuited, there would be
|
|
217
|
+
// nothing for `catchStep` to catch.
|
|
218
|
+
failedCommandResumes: () => {
|
|
219
|
+
const e = doAdd('add')(2, 3)
|
|
220
|
+
assert(typeof e !== 'function', e)
|
|
221
|
+
assertPure(e.continuation(error('declined')), error('declined'))
|
|
124
222
|
},
|
|
125
223
|
match: {
|
|
126
224
|
done: () => {
|
|
127
|
-
const r = next(pure(7))
|
|
225
|
+
const r = next(pure(ok(7)))
|
|
128
226
|
assert(r[0] === 'done', r)
|
|
129
|
-
assertEq(r[1], 7)
|
|
227
|
+
assertEq(r[1][1], 7)
|
|
130
228
|
},
|
|
131
229
|
cont: () => {
|
|
132
230
|
const r = next(doAdd('add')(2, 3))
|
|
133
231
|
assert(r[0] === 'cont', r)
|
|
134
|
-
assertEq(r[1], 5)
|
|
232
|
+
assertEq(r[1][1], 5)
|
|
135
233
|
const r2 = next(r[2](r[1]))
|
|
136
234
|
assert(r2[0] === 'done', r2)
|
|
137
|
-
assertEq(r2[1], 5)
|
|
235
|
+
assertEq(r2[1][1], 5)
|
|
138
236
|
},
|
|
139
237
|
ownCommand: () => {
|
|
140
238
|
// The same map the two cases below dispatch against: an own
|
|
@@ -142,7 +240,7 @@ export const proof = {
|
|
|
142
240
|
// inherited names, not a map that dispatches nothing.
|
|
143
241
|
const r = anyNext(doAny('add')(41))
|
|
144
242
|
assert(r[0] === 'cont', r)
|
|
145
|
-
assertEq(r[1], 42)
|
|
243
|
+
assertEq(r[1][1], 42)
|
|
146
244
|
},
|
|
147
245
|
// A `command` naming an `Object.prototype` member must not dispatch to
|
|
148
246
|
// the inherited value. `map['constructor']` is `Object` and
|
|
@@ -158,77 +256,259 @@ export const proof = {
|
|
|
158
256
|
},
|
|
159
257
|
},
|
|
160
258
|
},
|
|
259
|
+
partialMatch: {
|
|
260
|
+
// A command the runner does implement dispatches as usual.
|
|
261
|
+
implemented: () => {
|
|
262
|
+
const r = anyPartial(doAny('add')(41))
|
|
263
|
+
assert(r[0] === 'cont', r)
|
|
264
|
+
assertEq(r[1][1], 42)
|
|
265
|
+
},
|
|
266
|
+
// A command in the set with no handler is an *outcome*: the answer
|
|
267
|
+
// travels back through the ordinary continuation, and the program
|
|
268
|
+
// decides what a runner without that capability means for it.
|
|
269
|
+
declined: () => {
|
|
270
|
+
const r = anyPartial(doAny('sub')(1))
|
|
271
|
+
assert(r[0] === 'cont', r)
|
|
272
|
+
assert(r[1][0] === 'error' && r[1][1] === 'declined: sub', r)
|
|
273
|
+
assertPure(r[2](r[1]), error('declined: sub'))
|
|
274
|
+
},
|
|
275
|
+
// A command outside the set is a malformed node, not a capability the
|
|
276
|
+
// runner lacks, and still panics.
|
|
277
|
+
throw: {
|
|
278
|
+
unknownCommand: () => {
|
|
279
|
+
anyPartial(doAny('nope')(1))
|
|
280
|
+
},
|
|
281
|
+
},
|
|
282
|
+
},
|
|
283
|
+
pureOk: () => {
|
|
284
|
+
assertOk(pureResult(pureOk(5)), 5)
|
|
285
|
+
},
|
|
286
|
+
pureError: () => {
|
|
287
|
+
assertError(pureResult(pureError('nope')), 'nope')
|
|
288
|
+
},
|
|
161
289
|
step: {
|
|
162
|
-
|
|
163
|
-
|
|
290
|
+
ok: () => {
|
|
291
|
+
const e = step(pureOk(5), v => pureOk(v * 2))
|
|
292
|
+
assertOk(pureResult(e), 10)
|
|
293
|
+
},
|
|
294
|
+
// The continuation does not run on an `error`: `todo` throws if it
|
|
295
|
+
// does, so propagation is what keeps this proof passing.
|
|
296
|
+
propagates: () => {
|
|
297
|
+
const e = step(pureError('boom'), todo)
|
|
298
|
+
assertError(pureResult(e), 'boom')
|
|
299
|
+
},
|
|
300
|
+
// ...and the error that comes out is the very tuple that went in,
|
|
301
|
+
// rather than an equal one rebuilt to retag it.
|
|
302
|
+
errorTupleIsUnchanged: () => {
|
|
303
|
+
const e = pureError('boom')
|
|
304
|
+
assertEq(pureResult(step(e, todo)), pureResult(e))
|
|
164
305
|
},
|
|
165
306
|
chain: () => {
|
|
166
|
-
//
|
|
167
|
-
|
|
307
|
+
// Flat, one name per link — the chain of `step`s reads in
|
|
308
|
+
// evaluation order.
|
|
309
|
+
const x0 = step(pureOk(3), v => pureOk(v + 1))
|
|
310
|
+
const x1 = step(x0, v => pureOk(v * 2))
|
|
311
|
+
assertOk(pureResult(x1), 8)
|
|
168
312
|
},
|
|
313
|
+
// A `Do` node keeps its command, and the continuation resumes with the
|
|
314
|
+
// operation's `ok` value rather than with the `Result` around it.
|
|
169
315
|
overDo: () => {
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
316
|
+
const e = step(div(6, 3), v => pureOk(v * 10))
|
|
317
|
+
assertOk(run(e), 20)
|
|
318
|
+
},
|
|
319
|
+
// The same chain when the operation itself fails: the command was
|
|
320
|
+
// performed, its error propagates, and the continuation never runs.
|
|
321
|
+
overFailedDo: () => {
|
|
322
|
+
// `todo` never returns, so it pins none of the continuation's type
|
|
323
|
+
// parameters; the annotation supplies the operation set `run` needs.
|
|
324
|
+
/** @type {Effect<_DivOp, never, string>} */
|
|
325
|
+
const e = step(div(1, 0), todo)
|
|
326
|
+
assertError(run(e), 'div by zero')
|
|
327
|
+
},
|
|
328
|
+
// Adjacent links performing different commands: the operation sets
|
|
329
|
+
// union, so one runner interprets the whole chain.
|
|
330
|
+
joinsOperations: () => {
|
|
331
|
+
/** @type {Effect<_Op, number, string>} */
|
|
332
|
+
const e = step(div(6, 3), neg)
|
|
333
|
+
assertOk(run(e), -2)
|
|
334
|
+
},
|
|
335
|
+
mixedErrorsOk: () => {
|
|
336
|
+
assertOk(pureResult(checked(5)), 5)
|
|
337
|
+
},
|
|
338
|
+
// The first link's error type...
|
|
339
|
+
mixedErrorsFirst: () => {
|
|
340
|
+
assertError(pureResult(checked(-1)), 'not positive')
|
|
341
|
+
},
|
|
342
|
+
// ...and the second's, in the same chain and the same channel.
|
|
343
|
+
mixedErrorsSecond: () => {
|
|
344
|
+
assertError(pureResult(checked(50)), 50)
|
|
177
345
|
},
|
|
178
346
|
},
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
347
|
+
catchStep: {
|
|
348
|
+
recovers: () => {
|
|
349
|
+
const e = catchStep(pureError('boom'), m => pureOk(m.length))
|
|
350
|
+
assertOk(pureResult(e), 4)
|
|
182
351
|
},
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
352
|
+
// The success passes through untouched — `todo` proves the recovery
|
|
353
|
+
// does not run — and it is the same tuple, as in `step`'s mirror case.
|
|
354
|
+
preservesOk: () => {
|
|
355
|
+
const e = pureOk(5)
|
|
356
|
+
assertEq(pureResult(catchStep(e, todo)), pureResult(e))
|
|
357
|
+
},
|
|
358
|
+
// Recovery after a performed command, which is the shape a runner's
|
|
359
|
+
// `NotImplemented` takes: the program regains control and chooses what
|
|
360
|
+
// the failure means for it.
|
|
361
|
+
overFailedDo: () => {
|
|
362
|
+
const e = catchStep(div(1, 0), () => pureOk(0))
|
|
363
|
+
assertOk(run(e), 0)
|
|
364
|
+
},
|
|
365
|
+
// The success channel unions `T | R`: a recovery may yield a different
|
|
366
|
+
// type than the value it stands in for, and the error channel becomes
|
|
367
|
+
// `f`'s alone — `never` here, since every error is handled.
|
|
368
|
+
unionsSuccess: () => {
|
|
369
|
+
/** @type {Effect<never, number | string, never>} */
|
|
370
|
+
const e = catchStep(pureError('boom'), m => pureOk(m))
|
|
371
|
+
assertOk(pureResult(e), 'boom')
|
|
372
|
+
},
|
|
373
|
+
},
|
|
374
|
+
resultStep: {
|
|
375
|
+
// Both branches reach `f`, which decides the outcome — including
|
|
376
|
+
// turning an `error` into an `ok`.
|
|
377
|
+
ok: () => {
|
|
378
|
+
const e = resultStep(pureOk(5), ([tag]) => pureOk(tag))
|
|
379
|
+
assertOk(pureResult(e), 'ok')
|
|
380
|
+
},
|
|
381
|
+
error: () => {
|
|
382
|
+
const e = resultStep(pureError('boom'), ([tag]) => pureOk(tag))
|
|
383
|
+
assertOk(pureResult(e), 'error')
|
|
186
384
|
},
|
|
187
385
|
overDo: () => {
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
const r = next(mapStep(doAdd('add')(2, 3), v => v * 10))
|
|
191
|
-
assert(r[0] === 'cont', r)
|
|
192
|
-
assertEq(r[1], 5)
|
|
193
|
-
assertPure(r[2](r[1]), 50)
|
|
386
|
+
const e = resultStep(div(1, 0), ([tag]) => pureOk(tag))
|
|
387
|
+
assertOk(run(e), 'error')
|
|
194
388
|
},
|
|
195
389
|
},
|
|
196
390
|
historyStep: {
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
const
|
|
391
|
+
// The chain stays flat: the last link reads a value bound two links
|
|
392
|
+
// back out of the tuple rather than out of an enclosing closure.
|
|
393
|
+
chain: () => {
|
|
394
|
+
const h0 = history(pureOk(1))
|
|
395
|
+
const h1 = historyStep(h0, x => pureOk(x + 1))
|
|
396
|
+
const h2 = historyStep(h1, (y, x) => pureOk(`${x}${y}`))
|
|
397
|
+
assertOk(pureResult(mapStep(h2, ([z]) => z)), '12')
|
|
398
|
+
},
|
|
399
|
+
// The history carries `ok` values, so a later link reads them without
|
|
400
|
+
// asking whether they are there...
|
|
401
|
+
carriesValues: () => {
|
|
402
|
+
const h0 = historyStep(history(pureOk(3)), x => pureOk(x * 2))
|
|
403
|
+
const [result, param] = unwrapPure(h0)
|
|
201
404
|
assertEq(param, 3)
|
|
202
405
|
assertEq(result, 6)
|
|
203
406
|
},
|
|
407
|
+
// ...because a failed link short-circuits instead of contributing one.
|
|
408
|
+
propagates: () => {
|
|
409
|
+
const h0 = historyStep(history(pureOk(3)), () => pureError('boom'))
|
|
410
|
+
assertError(pureResult(historyStep(h0, todo)), 'boom')
|
|
411
|
+
},
|
|
412
|
+
// A failure in the effect the history starts from never reaches `f`.
|
|
413
|
+
propagatesFromHead: () => {
|
|
414
|
+
assertError(pureResult(historyStep(history(pureError('boom')), todo)), 'boom')
|
|
415
|
+
},
|
|
416
|
+
// Over a `Do` node: the captured value survives the command boundary.
|
|
204
417
|
overDo: () => {
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
assertEq(c[1], 5)
|
|
210
|
-
const o = runPure(c[2](c[1]))
|
|
211
|
-
assert(o.length === 1, o)
|
|
212
|
-
const [[result, param]] = o
|
|
213
|
-
assertEq(param, 5)
|
|
214
|
-
assertEq(result, 50)
|
|
418
|
+
const h0 = historyStep(history(div(6, 3)), r => pureOk(r * 10))
|
|
419
|
+
const [result, param] = run(h0)[1]
|
|
420
|
+
assertEq(param, 2)
|
|
421
|
+
assertEq(result, 20)
|
|
215
422
|
},
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
423
|
+
},
|
|
424
|
+
foldStep: {
|
|
425
|
+
empty: () => {
|
|
426
|
+
assertOk(pureResult(foldStep(pureOk([]), 10, x => s => pureOk(s + x))), 10)
|
|
427
|
+
},
|
|
428
|
+
threadsState: () => {
|
|
429
|
+
assertOk(pureResult(foldStep(pureOk([1, 2, 3, 4]), 0, x => s => pureOk(s + x))), 10)
|
|
430
|
+
},
|
|
431
|
+
// The first failure stops the fold: `4` never reaches the accumulator,
|
|
432
|
+
// and the error is the result.
|
|
433
|
+
shortCircuits: () => {
|
|
434
|
+
const e = foldStep(pureOk([1, 2, 4]),
|
|
435
|
+
0,
|
|
436
|
+
x => s => x === 2 ? pureError('two') : pureOk(s + x))
|
|
437
|
+
assertError(pureResult(e), 'two')
|
|
438
|
+
},
|
|
439
|
+
},
|
|
440
|
+
forEachStep: {
|
|
441
|
+
empty: () => {
|
|
442
|
+
assertOk(pureResult(forEachStep(pureOk([]), todo)), undefined)
|
|
443
|
+
},
|
|
444
|
+
runs: () => {
|
|
445
|
+
assertOk(pureResult(forEachStep(pureOk([1, 2, 3]), () => pureOk(undefined))), undefined)
|
|
446
|
+
},
|
|
447
|
+
// Where a `Result`-blind `forEachStep` would run every item regardless,
|
|
448
|
+
// this one stops — the difference a `void` accumulator hides.
|
|
449
|
+
stopsAtTheFirstError: () => {
|
|
450
|
+
const e = forEachStep(pureOk([1, 2, 3]),
|
|
451
|
+
x => x === 2 ? pureError('two') : pureOk(undefined))
|
|
452
|
+
assertError(pureResult(e), 'two')
|
|
453
|
+
},
|
|
454
|
+
},
|
|
455
|
+
unwrapStep: {
|
|
456
|
+
// An `ok` passes through untouched — what the panic empties is the
|
|
457
|
+
// error channel, so the value is still carried in an `ok`.
|
|
458
|
+
ok: () => {
|
|
459
|
+
assertOk(pureResult(unwrapStep(pureOk(5), show)), 5)
|
|
460
|
+
},
|
|
461
|
+
// An `error` is a panic — the policy the name exists to make greppable.
|
|
462
|
+
// It throws where the composition is written, since `mapStep` forces a
|
|
463
|
+
// `Pure` head immediately.
|
|
464
|
+
throw: {
|
|
465
|
+
error: () => {
|
|
466
|
+
unwrapStep(pureError('boom'), show)
|
|
467
|
+
},
|
|
468
|
+
},
|
|
469
|
+
},
|
|
470
|
+
mapStep: {
|
|
471
|
+
ok: () => {
|
|
472
|
+
assertOk(pureResult(mapStep(pureOk(3), v => v + 1)), 4)
|
|
473
|
+
},
|
|
474
|
+
// An `error` is passed through unchanged — the same tuple, and `f` is
|
|
475
|
+
// never applied to it.
|
|
476
|
+
error: () => {
|
|
477
|
+
const e = pureError('boom')
|
|
478
|
+
assertEq(pureResult(mapStep(e, todo)), pureResult(e))
|
|
479
|
+
},
|
|
480
|
+
// A projection over a `Do` node keeps the command intact and applies
|
|
481
|
+
// `f` to the operation's `ok` value when the continuation resumes.
|
|
482
|
+
overDo: () => {
|
|
483
|
+
assertOk(run(mapStep(div(6, 3), v => v * 10)), 20)
|
|
484
|
+
},
|
|
485
|
+
},
|
|
486
|
+
resultMapStep: {
|
|
487
|
+
// Both branches reach `f`, which is the difference from `mapStep`: the
|
|
488
|
+
// error is handed over rather than passed around.
|
|
489
|
+
ok: () => {
|
|
490
|
+
assertOk(pureResult(resultMapStep(pureOk(3), r => ok(r[0]))), 'ok')
|
|
491
|
+
},
|
|
492
|
+
error: () => {
|
|
493
|
+
assertOk(pureResult(resultMapStep(pureError('boom'), r => ok(r[0]))), 'error')
|
|
494
|
+
},
|
|
495
|
+
// `f` chooses the outgoing channel, so a failure can be turned into a
|
|
496
|
+
// success — the discard `mapStep` cannot express, written as a function
|
|
497
|
+
// that says it took both branches.
|
|
498
|
+
absorbs: () => {
|
|
499
|
+
assertOk(pureResult(resultMapStep(pureError('boom'), () => ok(0))), 0)
|
|
500
|
+
},
|
|
501
|
+
// ...and the reverse: a success can be rejected.
|
|
502
|
+
rejects: () => {
|
|
503
|
+
assertError(pureResult(resultMapStep(pureOk(3), () => error('no'))), 'no')
|
|
504
|
+
},
|
|
505
|
+
// Over a `Do` node the command survives and `f` runs on the operation's
|
|
506
|
+
// whole answer, including when that answer is the operation's failure.
|
|
507
|
+
overDo: () => {
|
|
508
|
+
assertOk(run(resultMapStep(div(6, 3), r => ok(r[1]))), 2)
|
|
509
|
+
},
|
|
510
|
+
overFailedDo: () => {
|
|
511
|
+
assertOk(run(resultMapStep(div(1, 0), r => ok(r[0]))), 'error')
|
|
232
512
|
},
|
|
233
513
|
},
|
|
234
514
|
}
|