functionalscript 0.46.0 → 0.47.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.
- package/fjs/basen/cbase32/module.f.mjs +18 -10
- package/fjs/basen/cbase32/proof.f.d.mts +3 -0
- package/fjs/basen/cbase32/proof.f.mjs +24 -2
- package/fjs/basen/proof.f.mjs +8 -16
- package/fjs/bnf/ll1/proof.f.d.mts +2 -0
- package/fjs/bnf/ll1/proof.f.mjs +33 -18
- package/fjs/bnf/module.f.d.mts +2 -2
- package/fjs/bnf/module.f.mjs +2 -2
- package/fjs/cas/proof.f.mjs +4 -26
- package/fjs/ci/common/module.f.mjs +6 -0
- package/fjs/ci/config/module.f.d.mts +7 -7
- package/fjs/ci/config/module.f.mjs +7 -7
- package/fjs/cli/module.f.mjs +14 -10
- package/fjs/crypto/vdf/module.f.d.mts +0 -1
- package/fjs/crypto/vdf/module.f.mjs +3 -14
- package/fjs/dev/module.f.mjs +10 -20
- package/fjs/djs/parser/module.f.d.mts +101 -53
- package/fjs/djs/parser/module.f.mjs +732 -525
- package/fjs/djs/parser/proof.f.d.mts +6 -0
- package/fjs/djs/parser/proof.f.mjs +315 -11
- package/fjs/djs/parser/types.d.ts +29 -2
- package/fjs/djs/tokenizer/module.f.mjs +122 -105
- package/fjs/djs/tokenizer/proof.f.mjs +17 -0
- package/fjs/djs/transpiler/module.f.mjs +4 -5
- package/fjs/djs/transpiler/proof.f.d.mts +2 -0
- package/fjs/djs/transpiler/proof.f.mjs +25 -0
- package/fjs/edag/amnesia/module.f.d.mts +20 -0
- package/fjs/edag/amnesia/module.f.mjs +303 -0
- package/fjs/edag/amnesia/proof.f.d.mts +79 -0
- package/fjs/edag/amnesia/proof.f.mjs +573 -0
- package/fjs/edag/amnesia/types.d.ts +15 -0
- package/fjs/edag/module.f.d.mts +410 -0
- package/fjs/edag/module.f.mjs +559 -0
- package/fjs/edag/proof.f.d.mts +116 -0
- package/fjs/edag/proof.f.mjs +686 -0
- package/fjs/edag/types.d.ts +55 -0
- package/fjs/effects/node/memory/module.d.mts +37 -3
- package/fjs/effects/node/memory/module.mjs +38 -4
- package/fjs/effects/node/memory/proof.d.mts +5 -2
- package/fjs/effects/node/memory/proof.mjs +36 -6
- package/fjs/effects/node/module.d.mts +9 -1
- package/fjs/effects/node/module.f.d.mts +39 -0
- package/fjs/effects/node/module.f.mjs +46 -0
- package/fjs/effects/node/module.mjs +234 -32
- package/fjs/effects/node/types.d.ts +28 -2
- package/fjs/effects/node/virtual/module.f.d.mts +19 -10
- package/fjs/effects/node/virtual/module.f.mjs +246 -32
- package/fjs/effects/node/virtual/proof.f.d.mts +17 -2
- package/fjs/effects/node/virtual/proof.f.mjs +232 -8
- package/fjs/effects/node/virtual/types.d.ts +57 -1
- package/fjs/emergent_testing/browser/proof.d.mts +55 -0
- package/fjs/emergent_testing/browser/proof.mjs +352 -0
- package/fjs/emergent_testing/browser/species.proof.d.mts +4 -0
- package/fjs/emergent_testing/browser/species.proof.mjs +45 -0
- package/fjs/emergent_testing/browser.d.mts +79 -0
- package/fjs/emergent_testing/browser.mjs +432 -0
- package/fjs/emergent_testing/proof.f.d.mts +1 -1
- package/fjs/emergent_testing/proof.f.mjs +2 -2
- package/fjs/js/string_escape/module.f.d.mts +47 -0
- package/fjs/js/string_escape/module.f.mjs +95 -0
- package/fjs/js/string_escape/proof.f.d.mts +8 -0
- package/fjs/js/string_escape/proof.f.mjs +68 -0
- package/fjs/js/tokenizer/module.f.mjs +14 -12
- package/fjs/mcp/cas/module.f.d.mts +1 -7
- package/fjs/mcp/cas/module.f.mjs +24 -15
- package/fjs/mcp/evo/module.f.mjs +9 -24
- package/fjs/mcp/module.f.d.mts +3 -1
- package/fjs/mcp/module.f.mjs +4 -2
- package/fjs/media/json/rtti/module.f.d.mts +2 -2
- package/fjs/media/json/rtti/module.f.mjs +1 -1
- package/fjs/media/json/schema/module.f.d.mts +15 -5
- package/fjs/media/json/schema/module.f.mjs +42 -15
- package/fjs/media/json/schema/proof.f.d.mts +10 -1
- package/fjs/media/json/schema/proof.f.mjs +88 -16
- package/fjs/media/json/serializer/module.f.mjs +14 -26
- package/fjs/media/json/types.d.ts +1 -1
- package/fjs/media/lock/module.f.d.mts +8 -3
- package/fjs/media/lock/module.f.mjs +9 -3
- package/fjs/media/lock/proof.f.d.mts +1 -1
- package/fjs/media/lock/proof.f.mjs +2 -2
- package/fjs/media/lock/types.d.ts +1 -3
- package/fjs/media/module.f.d.mts +20 -13
- package/fjs/media/module.f.mjs +29 -16
- package/fjs/media/note/module.f.d.mts +14 -7
- package/fjs/media/note/module.f.mjs +14 -7
- package/fjs/media/note/proof.f.mjs +1 -1
- package/fjs/media/proof.f.mjs +5 -5
- package/fjs/media/revision/module.f.d.mts +8 -2
- package/fjs/media/revision/module.f.mjs +9 -3
- package/fjs/media/revision/proof.f.mjs +1 -1
- package/fjs/media/revision/types.d.ts +1 -1
- package/fjs/media/type/module.f.d.mts +20 -0
- package/fjs/media/type/module.f.mjs +74 -0
- package/fjs/media/type/proof.f.d.mts +5 -0
- package/fjs/media/type/proof.f.mjs +24 -1
- package/fjs/module.f.mjs +6 -0
- package/fjs/path/module.f.d.mts +60 -3
- package/fjs/path/module.f.mjs +138 -14
- package/fjs/path/proof.f.d.mts +3 -0
- package/fjs/path/proof.f.mjs +298 -2
- package/fjs/protocol/json_rpc/module.f.d.mts +65 -28
- package/fjs/protocol/json_rpc/module.f.mjs +49 -13
- package/fjs/protocol/json_rpc/proof.f.d.mts +4 -0
- package/fjs/protocol/json_rpc/proof.f.mjs +29 -0
- package/fjs/protocol/json_rpc/types.d.ts +12 -2
- package/fjs/protocol/mcp/module.f.d.mts +89 -54
- package/fjs/protocol/mcp/module.f.mjs +110 -56
- package/fjs/protocol/mcp/proof.f.d.mts +13 -0
- package/fjs/protocol/mcp/proof.f.mjs +107 -7
- package/fjs/protocol/mcp/stdio/module.f.mjs +3 -4
- package/fjs/protocol/mcp/stdio/proof.f.mjs +2 -2
- package/fjs/protocol/mcp/types.d.ts +14 -2
- package/fjs/text/utf8/module.f.mjs +27 -1
- package/fjs/text/utf8/proof.f.mjs +37 -0
- package/fjs/types/array/module.f.mjs +18 -11
- package/fjs/types/array/proof.f.mjs +27 -1
- package/fjs/types/bigfloat/module.f.d.mts +48 -9
- package/fjs/types/bigfloat/module.f.mjs +147 -59
- package/fjs/types/bigfloat/proof.f.d.mts +2 -0
- package/fjs/types/bigfloat/proof.f.mjs +136 -4
- package/fjs/types/bigfloat/types.d.ts +17 -0
- package/fjs/types/function/module.f.d.mts +11 -2
- package/fjs/types/function/module.f.mjs +20 -2
- package/fjs/types/function/proof.f.mjs +5 -1
- package/fjs/types/phantom/types.d.ts +21 -0
- package/fjs/types/result/module.f.d.mts +4 -4
- package/fjs/types/result/module.f.mjs +2 -2
- package/fjs/types/result/proof.f.d.mts +6 -2
- package/fjs/types/result/proof.f.mjs +17 -1
- package/fjs/types/rtti/common/module.f.d.mts +78 -5
- package/fjs/types/rtti/common/module.f.mjs +168 -3
- package/fjs/types/rtti/common/proof.f.d.mts +14 -0
- package/fjs/types/rtti/common/proof.f.mjs +61 -2
- package/fjs/types/rtti/common/types.d.ts +23 -1
- package/fjs/types/rtti/data/module.f.d.mts +57 -1
- package/fjs/types/rtti/data/module.f.mjs +241 -47
- package/fjs/types/rtti/data/proof.f.d.mts +10 -0
- package/fjs/types/rtti/data/proof.f.mjs +272 -39
- package/fjs/types/rtti/data/types.d.ts +14 -7
- package/fjs/types/rtti/host.proof.d.mts +33 -0
- package/fjs/types/rtti/host.proof.mjs +170 -0
- package/fjs/types/rtti/module.f.d.mts +51 -6
- package/fjs/types/rtti/module.f.mjs +50 -3
- package/fjs/types/rtti/parse/module.f.d.mts +31 -24
- package/fjs/types/rtti/parse/module.f.mjs +197 -59
- package/fjs/types/rtti/parse/proof.f.d.mts +37 -2
- package/fjs/types/rtti/parse/proof.f.mjs +171 -47
- package/fjs/types/rtti/proof.f.d.mts +6 -0
- package/fjs/types/rtti/proof.f.mjs +42 -1
- package/fjs/types/rtti/ts/module.f.d.mts +9 -3
- package/fjs/types/rtti/ts/module.f.mjs +68 -15
- package/fjs/types/rtti/ts/proof.f.d.mts +42 -2
- package/fjs/types/rtti/ts/proof.f.mjs +139 -6
- package/fjs/types/rtti/ts/types.d.ts +164 -16
- package/fjs/types/rtti/types.d.ts +31 -3
- package/fjs/types/rtti/validate/module.f.d.mts +120 -0
- package/fjs/types/rtti/validate/module.f.mjs +351 -0
- package/fjs/types/rtti/validate/proof.f.d.mts +154 -0
- package/fjs/types/rtti/validate/proof.f.mjs +781 -0
- package/fjs/types/ts/types.d.ts +4 -0
- package/fjs/web/module.f.d.mts +115 -0
- package/fjs/web/module.f.mjs +581 -0
- package/fjs/web/proof.f.d.mts +39 -0
- package/fjs/web/proof.f.mjs +413 -0
- package/fjs/web/types.d.ts +39 -0
- package/fjs/website/browser-prepare.d.mts +5 -0
- package/fjs/website/browser-prepare.mjs +115 -0
- package/fjs/website/browser-source.d.mts +37 -0
- package/fjs/website/browser-source.mjs +202 -0
- package/fjs/website/browser-source.proof.d.mts +31 -0
- package/fjs/website/browser-source.proof.mjs +162 -0
- package/fjs/website/browser.d.mts +5 -0
- package/fjs/website/browser.mjs +8 -0
- package/fjs/website/module.f.d.mts +3 -3
- package/fjs/website/module.f.mjs +62 -9
- package/fjs/website/proof.f.d.mts +4 -0
- package/fjs/website/proof.f.mjs +18 -3
- package/package.json +2 -3
|
@@ -3,46 +3,49 @@
|
|
|
3
3
|
*
|
|
4
4
|
* @module
|
|
5
5
|
*
|
|
6
|
-
* @import { BigFloat } from './types.ts'
|
|
6
|
+
* @import { BigFloat, Format } from './types.ts'
|
|
7
|
+
* @import { Nullable } from '../nullable/types.ts'
|
|
7
8
|
*/
|
|
8
9
|
|
|
9
|
-
import { abs, sign } from '../bigint/module.f.mjs'
|
|
10
|
+
import { abs, bitLength, mask, sign } from '../bigint/module.f.mjs'
|
|
10
11
|
|
|
11
|
-
/**
|
|
12
|
+
/**
|
|
13
|
+
* A magnitude that has been truncated, paired with what was cut off: the exact
|
|
14
|
+
* value is `m * 2^e` when `r` is `0n`, and strictly between `m * 2^e` and
|
|
15
|
+
* `(m + 1) * 2^e` otherwise. Only `r === 0n` is ever asked, so any non-zero
|
|
16
|
+
* `r` — a division remainder, the bits a shift dropped, or both — says the
|
|
17
|
+
* same thing.
|
|
18
|
+
*
|
|
19
|
+
* @typedef {readonly [BigFloat, bigint]} _BigFloatWithRemainder
|
|
20
|
+
*/
|
|
12
21
|
|
|
13
|
-
|
|
14
|
-
const
|
|
22
|
+
/** @type {(exp: number) => bigint} */
|
|
23
|
+
const twoPow = exp => 1n << BigInt(exp)
|
|
15
24
|
|
|
16
25
|
/**
|
|
17
|
-
*
|
|
18
|
-
* until
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
* @
|
|
26
|
+
* Doubles the mantissa magnitude one bit at a time, compensating the exponent,
|
|
27
|
+
* until it reaches `min`; the sign is restored on return. A zero mantissa is
|
|
28
|
+
* returned unchanged: it can never reach a lower bound, so shifting it would
|
|
29
|
+
* not terminate.
|
|
30
|
+
*
|
|
31
|
+
* There is no downward twin: a magnitude that overshoots is brought back by
|
|
32
|
+
* `truncate`, which has to know what it cut off and so cannot be a shift loop.
|
|
33
|
+
*
|
|
34
|
+
* @type {(_: BigFloat) => (min: bigint) => BigFloat}
|
|
26
35
|
*/
|
|
27
|
-
const
|
|
36
|
+
const increaseMantissa = ([m, e]) => min => {
|
|
28
37
|
if (m === 0n) {
|
|
29
38
|
return [m, e]
|
|
30
39
|
}
|
|
31
40
|
const s = sign(m)
|
|
32
41
|
m = abs(m)
|
|
33
|
-
while (
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
}
|
|
37
|
-
m = shift(m)
|
|
38
|
-
e += de
|
|
42
|
+
while (m < min) {
|
|
43
|
+
m <<= 1n
|
|
44
|
+
e -= 1
|
|
39
45
|
}
|
|
46
|
+
return [BigInt(s) * m, e]
|
|
40
47
|
}
|
|
41
48
|
|
|
42
|
-
const increaseMantissa = normalizeMantissa(m => m << 1n, -1, (m, min) => m >= min)
|
|
43
|
-
|
|
44
|
-
const decreaseMantissa = normalizeMantissa(m => m >> 1n, +1, (m, max) => m < max)
|
|
45
|
-
|
|
46
49
|
/** @type {(base: bigint) => (exp: number) => bigint} */
|
|
47
50
|
const pow = base => exp => base ** BigInt(exp)
|
|
48
51
|
|
|
@@ -54,6 +57,21 @@ export const multiply = ([m, e]) => mul => [m * mul, e]
|
|
|
54
57
|
/** @type {(_: BigFloat) => (div: bigint) => _BigFloatWithRemainder} */
|
|
55
58
|
const divide = ([m, e]) => div => [[m / div, e], m % div]
|
|
56
59
|
|
|
60
|
+
/**
|
|
61
|
+
* IEEE-754 binary64 — the format of a JavaScript `number`.
|
|
62
|
+
*
|
|
63
|
+
* Both exponents are *ulp* exponents, the same units a `BigFloat`'s own
|
|
64
|
+
* exponent is in: `minExp` is the exponent of the smallest subnormal
|
|
65
|
+
* (`2^-1074`), not the smallest normal's `-1022`, and `maxExp` is the exponent
|
|
66
|
+
* the largest finite value carries once its mantissa fills `precision` bits
|
|
67
|
+
* (`(2^53 - 1) * 2^971`). The two conventions differ by the precision, and a
|
|
68
|
+
* `Format` that mixes them is off by exactly that much, so this one never
|
|
69
|
+
* leaves `BigFloat`'s units.
|
|
70
|
+
*
|
|
71
|
+
* @type {Format}
|
|
72
|
+
*/
|
|
73
|
+
export const binary64 = { precision: 53, minExp: -1074, maxExp: 971 }
|
|
74
|
+
|
|
57
75
|
/**
|
|
58
76
|
* Runs `f` on the magnitude `[abs(m), e]` and restores the sign of `m` on the
|
|
59
77
|
* result: operations on signed mantissas factor through the magnitude.
|
|
@@ -63,25 +81,66 @@ const divide = ([m, e]) => div => [[m / div, e], m % div]
|
|
|
63
81
|
const withSign = (m, e) => f => multiply(f([abs(m), e]))(BigInt(sign(m)))
|
|
64
82
|
|
|
65
83
|
/**
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
*
|
|
84
|
+
* Truncates a magnitude to `precision + 1` bits, folding the bits it drops
|
|
85
|
+
* into the remainder. A magnitude already that narrow is returned unchanged.
|
|
86
|
+
*
|
|
87
|
+
* @type {(precision: number) => (_: _BigFloatWithRemainder) => _BigFloatWithRemainder}
|
|
88
|
+
*/
|
|
89
|
+
const truncate = precision => ([[m, e], r]) => {
|
|
90
|
+
const k = bitLength(m) - BigInt(precision + 1)
|
|
91
|
+
return k <= 0n ? [[m, e], r] : [[m >> k, e + Number(k)], r | (m & mask(k))]
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Scales a non-zero decimal magnitude `dm * 10^de` into `[[m, e], r]` where
|
|
96
|
+
* `m` holds exactly `precision + 1` bits.
|
|
97
|
+
*
|
|
98
|
+
* One bit more than the precision is kept on purpose. Truncating to *any*
|
|
99
|
+
* position at or below the bit the rounding turns on, plus a remainder saying
|
|
100
|
+
* whether anything was cut off, is everything a single correctly-rounded
|
|
101
|
+
* decision needs — at this precision or at any coarser one, which is what
|
|
102
|
+
* makes the subnormal range reachable without rounding twice.
|
|
70
103
|
*
|
|
71
|
-
* @type {(magnitude: BigFloat) =>
|
|
104
|
+
* @type {(precision: number) => (magnitude: BigFloat) => _BigFloatWithRemainder}
|
|
72
105
|
*/
|
|
73
|
-
const
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
106
|
+
const scale = precision => ([dm, de]) => {
|
|
107
|
+
const lo = twoPow(precision)
|
|
108
|
+
if (de >= 0) {
|
|
109
|
+
// `dm * 10^de` is a whole number, so nothing is lost until `truncate`.
|
|
110
|
+
return truncate(precision)([increaseMantissa([dm * pow5(de), de])(lo), 0n])
|
|
111
|
+
}
|
|
112
|
+
const p5 = pow5(-de)
|
|
113
|
+
return truncate(precision)(divide(increaseMantissa([dm, de])(p5 * lo))(p5))
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Rounds a truncated magnitude to a multiple of `2^(e + k)`, ties to even.
|
|
118
|
+
*
|
|
119
|
+
* `k` is at least 1, so the bit the decision turns on is still inside `m`:
|
|
120
|
+
* `dropped` against `half` is the comparison against the midpoint, and a
|
|
121
|
+
* non-zero remainder is what lifts an apparent midpoint above it.
|
|
122
|
+
*
|
|
123
|
+
* @type {(k: number) => (_: _BigFloatWithRemainder) => BigFloat}
|
|
124
|
+
*/
|
|
125
|
+
const round = k => ([[m, e], r]) => {
|
|
126
|
+
const kb = BigInt(k)
|
|
127
|
+
const q = m >> kb
|
|
128
|
+
const dropped = m & mask(kb)
|
|
129
|
+
const half = 1n << (kb - 1n)
|
|
130
|
+
const up = dropped > half || (dropped === half && (r !== 0n || (q & 1n) === 1n))
|
|
131
|
+
return [up ? q + 1n : q, e + k]
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Re-normalizes a magnitude that rounding carried out of `precision` bits:
|
|
136
|
+
* rounding `2^precision - 1` up yields exactly `2^precision`, one bit wider
|
|
137
|
+
* than the caller promises. The bit shifted out is always 0 there, so the
|
|
138
|
+
* value is unchanged and no second rounding decision is needed.
|
|
139
|
+
*
|
|
140
|
+
* @type {(precision: number) => (magnitude: BigFloat) => BigFloat}
|
|
141
|
+
*/
|
|
142
|
+
const renormalize = precision => ([m, e]) =>
|
|
143
|
+
m === twoPow(precision) ? [m >> 1n, e + 1] : [m, e]
|
|
85
144
|
|
|
86
145
|
/**
|
|
87
146
|
* Converts a decimal big-float `m * 10^e` into the nearest binary big-float,
|
|
@@ -92,29 +151,58 @@ const round53 = ([[m, e], r]) =>
|
|
|
92
151
|
* even when rounding carries out of the top bit.
|
|
93
152
|
*
|
|
94
153
|
* The **exponent is unbounded**: this is the correctly-rounded 53-bit value,
|
|
95
|
-
* not a `number`.
|
|
96
|
-
*
|
|
97
|
-
*
|
|
98
|
-
*
|
|
99
|
-
* twice and can land an ulp
|
|
100
|
-
*
|
|
101
|
-
*
|
|
154
|
+
* not a `number`. A result too large for a `double` is not turned into an
|
|
155
|
+
* infinity, and one below the normal range keeps all 53 bits instead of the
|
|
156
|
+
* fewer a subnormal carries. That is the honest answer when the target is not
|
|
157
|
+
* a `double` — and the wrong starting point when it is, because rounding one
|
|
158
|
+
* of these results onto the subnormal grid rounds twice and can land an ulp
|
|
159
|
+
* away from the correctly-rounded `double`. Use {@link tryDecToFormat} with
|
|
160
|
+
* {@link binary64} for that; it rounds once.
|
|
102
161
|
*
|
|
103
162
|
* @type {(dec: BigFloat) => BigFloat}
|
|
104
163
|
*/
|
|
105
|
-
export const decToBin =
|
|
106
|
-
if (
|
|
164
|
+
export const decToBin = ([dm, de]) => {
|
|
165
|
+
if (dm === 0n) {
|
|
166
|
+
return [0n, 0]
|
|
167
|
+
}
|
|
168
|
+
const { precision } = binary64
|
|
169
|
+
return withSign(dm, de)(magnitude =>
|
|
170
|
+
renormalize(precision)(round(1)(scale(precision)(magnitude))))
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Converts a decimal big-float `m * 10^e` into the nearest value of `format`,
|
|
175
|
+
* rounding ties to even, or `null` when the magnitude is too large for the
|
|
176
|
+
* format to hold — the caller decides what an overflow becomes, since
|
|
177
|
+
* `BigFloat` has no encoding for an infinity.
|
|
178
|
+
*
|
|
179
|
+
* The result is a multiple of `2^format.minExp` carrying **at most**
|
|
180
|
+
* `format.precision` significant bits: exactly that many above the format's
|
|
181
|
+
* normal range, fewer below it, and `[0n, 0]` when the value rounds away
|
|
182
|
+
* entirely. Underflow to zero is not a separate signal, because `[0n, 0]` is
|
|
183
|
+
* the correctly-rounded answer rather than a failure to produce one.
|
|
184
|
+
*
|
|
185
|
+
* The rounding happens **once**, on the exact decimal, which is the whole
|
|
186
|
+
* point of taking the format rather than post-processing {@link decToBin}. The
|
|
187
|
+
* grid it rounds onto is `2^max(minExp, e + 1)`, where `e + 1` is the exponent
|
|
188
|
+
* a full-precision result would carry: full precision above the normal range's
|
|
189
|
+
* floor, shrinking by a bit per binade below it, down to none at all. Rounding
|
|
190
|
+
* to `precision` bits first and onto that grid afterwards is two roundings,
|
|
191
|
+
* and the first can manufacture a midpoint the true value only approached.
|
|
192
|
+
*
|
|
193
|
+
* @type {(format: Format) => (dec: BigFloat) => Nullable<BigFloat>}
|
|
194
|
+
*/
|
|
195
|
+
export const tryDecToFormat = ({ precision, minExp, maxExp }) => ([dm, de]) => {
|
|
196
|
+
if (dm === 0n) {
|
|
107
197
|
return [0n, 0]
|
|
108
198
|
}
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
return
|
|
199
|
+
const scaled = scale(precision)([abs(dm), de])
|
|
200
|
+
const [, e] = scaled[0]
|
|
201
|
+
const [m, resultE] = renormalize(precision)(round(Math.max(minExp - e, 1))(scaled))
|
|
202
|
+
if (bitLength(m) + BigInt(resultE) > BigInt(precision + maxExp)) {
|
|
203
|
+
return null
|
|
114
204
|
}
|
|
115
|
-
|
|
116
|
-
const [m, e] = increaseMantissa(dec)(p * twoPow53)
|
|
117
|
-
return withSign(m, e)(magnitude => round53(divide(magnitude)(p)))
|
|
205
|
+
return m === 0n ? [0n, 0] : multiply([m, resultE])(BigInt(sign(dm)))
|
|
118
206
|
}
|
|
119
207
|
|
|
120
208
|
export const proof = {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { decToBin } from './module.f.mjs'
|
|
2
|
-
import { assertEq } from '../../asserts/module.f.mjs'
|
|
3
|
-
import { bitLength } from '../bigint/module.f.mjs'
|
|
1
|
+
import { binary64, decToBin, tryDecToFormat } from './module.f.mjs'
|
|
2
|
+
import { assert, assertEq, assertStructurallySame } from '../../asserts/module.f.mjs'
|
|
3
|
+
import { abs, bitLength } from '../bigint/module.f.mjs'
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Checks `decToBin`'s postcondition on a non-zero result: the mantissa holds
|
|
@@ -17,6 +17,75 @@ const assertRounded = dec => value => {
|
|
|
17
17
|
assertEq(m << BigInt(e), value, m.toString(2))
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
+
const toBinary64 = tryDecToFormat(binary64)
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* The exact decimal for the dyadic value `m * 2^k`, as a `BigFloat` this
|
|
24
|
+
* module's own functions take: `2^k = 5^-k * 10^k` for a negative `k`, so no
|
|
25
|
+
* decimal digit is ever approximated on the way in. Every boundary below is
|
|
26
|
+
* stated as a power of two, which is the grid the format is defined on.
|
|
27
|
+
*
|
|
28
|
+
* @type {(m: bigint, k: number) => readonly [bigint, number]}
|
|
29
|
+
*/
|
|
30
|
+
const dyadic = (m, k) => k >= 0 ? [m << BigInt(k), 0] : [m * 5n ** BigInt(-k), k]
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Asserts that `toBinary64` answers exactly `[m, e]` for the exact decimal of
|
|
34
|
+
* `dyadic(dm, dk)`, and that the answer respects the format: on the
|
|
35
|
+
* `2^-1074` grid, and no wider than 53 bits.
|
|
36
|
+
*
|
|
37
|
+
* @type {(dyad: readonly [bigint, number]) => (expected: readonly [bigint, number]) => void}
|
|
38
|
+
*/
|
|
39
|
+
const assertBinary64 = ([dm, dk]) => expected => {
|
|
40
|
+
const result = toBinary64(dyadic(dm, dk))
|
|
41
|
+
assertStructurallySame(result, expected, [dm, dk])
|
|
42
|
+
const [m, e] = expected
|
|
43
|
+
assert(e >= binary64.minExp, expected)
|
|
44
|
+
assert(bitLength(abs(m)) <= BigInt(binary64.precision), expected)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Asserts that the exact decimal of `dyadic(dm, dk)` overflows the format.
|
|
49
|
+
*
|
|
50
|
+
* @type {(dyad: readonly [bigint, number]) => void}
|
|
51
|
+
*/
|
|
52
|
+
const assertOverflow = ([dm, dk]) => assertEq(toBinary64(dyadic(dm, dk)), null, [dm, dk])
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Cross-checks `toBinary64` against `Number(decimal)`, which the language
|
|
56
|
+
* specifies to be correctly rounded — the same claim this function makes, so
|
|
57
|
+
* disagreement on any input is a defect in one of them.
|
|
58
|
+
*
|
|
59
|
+
* A `null` stands for an infinity, which `BigFloat` cannot encode. `-0` is
|
|
60
|
+
* folded into `0` by the addition: a bigint mantissa has no signed zero, so
|
|
61
|
+
* the sign of an underflowed value is information this representation does
|
|
62
|
+
* not carry.
|
|
63
|
+
*
|
|
64
|
+
* @type {(dec: readonly [bigint, number]) => void}
|
|
65
|
+
*/
|
|
66
|
+
const assertSameAsNumber = ([dm, de]) => {
|
|
67
|
+
const expected = Number(`${dm}e${de}`)
|
|
68
|
+
const result = toBinary64([dm, de])
|
|
69
|
+
const actual = result === null
|
|
70
|
+
? (dm < 0n ? -Infinity : Infinity)
|
|
71
|
+
: Number(result[0]) * 2 ** result[1]
|
|
72
|
+
assertEq(actual + 0, expected + 0, [dm, de, result])
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** @type {(from: number) => (to: number) => readonly number[]} */
|
|
76
|
+
const range = from => to => Array.from({ length: to - from + 1 }, (_, i) => from + i)
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* A deterministic corpus of decimals: ten mantissas of both signs against
|
|
80
|
+
* every decimal exponent from below the subnormal range to past the top of
|
|
81
|
+
* the format, so every branch of the conversion is crossed by construction.
|
|
82
|
+
*
|
|
83
|
+
* @type {readonly (readonly [bigint, number])[]}
|
|
84
|
+
*/
|
|
85
|
+
const corpus = [1n, 2n, 3n, 5n, 7n, 9n, 10n, 4999999999999999n, 5000000000000001n, 12345678901234567890n]
|
|
86
|
+
.flatMap(dm => [dm, -dm])
|
|
87
|
+
.flatMap(dm => range(-340)(320).map(de => /** @type {readonly [bigint, number]} */([dm, de])))
|
|
88
|
+
|
|
20
89
|
export const proof = {
|
|
21
90
|
decToBin: [
|
|
22
91
|
() => {
|
|
@@ -260,5 +329,68 @@ export const proof = {
|
|
|
260
329
|
assertRounded([0b11_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1101n, 0])(
|
|
261
330
|
0b11_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1100n)
|
|
262
331
|
},
|
|
263
|
-
]
|
|
332
|
+
],
|
|
333
|
+
// `tryDecToFormat(binary64)` rounds once, onto the grid the target format
|
|
334
|
+
// actually has. Every case below is stated as an exact decimal for a
|
|
335
|
+
// dyadic value, so the input names a point on that grid or a known
|
|
336
|
+
// distance from one.
|
|
337
|
+
tryDecToFormat: [
|
|
338
|
+
// The case this function exists for. One part in 2^126 below the
|
|
339
|
+
// midpoint between the subnormals 3*2^-1074 and 4*2^-1074: the
|
|
340
|
+
// correctly-rounded answer is 3, and `decToBin`'s 53-bit answer is the
|
|
341
|
+
// midpoint itself, which rounds the other way because 3 is odd.
|
|
342
|
+
() => {
|
|
343
|
+
const dec = /** @type {const} */([(7n * 2n ** 125n - 1n) * 5n ** 1200n, -1200])
|
|
344
|
+
assertStructurallySame(decToBin(dec), [7881299347898368n, -1125], 'the midpoint decToBin lands on')
|
|
345
|
+
assertStructurallySame(toBinary64(dec), [3n, -1074], 'rounded once, below the midpoint')
|
|
346
|
+
},
|
|
347
|
+
// minExp exactly: the smallest value the format holds.
|
|
348
|
+
() => assertBinary64([1n, -1074])([1n, -1074]),
|
|
349
|
+
() => assertBinary64([3n, -1074])([3n, -1074]),
|
|
350
|
+
// Half of it is an exact tie, and 0 is the even side.
|
|
351
|
+
() => assertBinary64([1n, -1075])([0n, 0]),
|
|
352
|
+
// A hair above that tie rounds up; a hair below rounds down.
|
|
353
|
+
() => assertEq(toBinary64([5n ** 1075n + 1n, -1075])?.[0], 1n),
|
|
354
|
+
() => assertEq(toBinary64([5n ** 1075n - 1n, -1075])?.[0], 0n),
|
|
355
|
+
// Ties inside the subnormal range go to even in both directions.
|
|
356
|
+
() => assertBinary64([7n, -1075])([4n, -1074]),
|
|
357
|
+
() => assertBinary64([11n, -1075])([6n, -1074]),
|
|
358
|
+
// minExp + 52: the smallest normal, where full precision resumes. Its
|
|
359
|
+
// mantissa fills all 53 bits and its exponent is still minExp.
|
|
360
|
+
() => assertBinary64([1n, -1022])([1n << 52n, -1074]),
|
|
361
|
+
() => assertBinary64([(1n << 52n) - 1n, -1074])([(1n << 52n) - 1n, -1074]),
|
|
362
|
+
// maxExp exactly: the largest finite value.
|
|
363
|
+
() => assertBinary64([(1n << 53n) - 1n, 971])([(1n << 53n) - 1n, 971]),
|
|
364
|
+
() => assertBinary64([-((1n << 53n) - 1n), 971])([-((1n << 53n) - 1n), 971]),
|
|
365
|
+
// Rounding up out of the top bit has to come back as `precision` bits,
|
|
366
|
+
// not `precision + 1`. Nothing else here catches a missing
|
|
367
|
+
// re-normalization: the overflow check is invariant under it
|
|
368
|
+
// (`54 + (e + 1)` is `53 + (e + 2)`) and the value is the same either
|
|
369
|
+
// way, so only the width assertion sees it.
|
|
370
|
+
() => assertBinary64([(1n << 54n) - 1n, -54])([1n << 52n, -52]),
|
|
371
|
+
() => assertBinary64([-((1n << 54n) - 1n), -54])([-(1n << 52n), -52]),
|
|
372
|
+
// Past it: 2^1024 outright, and the midpoint below it, which overflows
|
|
373
|
+
// by rounding up rather than by magnitude.
|
|
374
|
+
() => assertOverflow([1n, 1024]),
|
|
375
|
+
() => assertOverflow([-1n, 1024]),
|
|
376
|
+
() => assertOverflow([(1n << 54n) - 1n, 970]),
|
|
377
|
+
() => assertBinary64([(((1n << 54n) - 1n) << 970n) - 1n, 0])([(1n << 53n) - 1n, 971]),
|
|
378
|
+
// Underflow is an answer, not a failure: it is the correctly-rounded
|
|
379
|
+
// one. A bigint mantissa has no signed zero, so both signs give `0n`.
|
|
380
|
+
() => assertBinary64([1n, -1200])([0n, 0]),
|
|
381
|
+
() => assertBinary64([-1n, -1200])([0n, 0]),
|
|
382
|
+
() => assertBinary64([0n, 0])([0n, 0]),
|
|
383
|
+
() => assertEq(toBinary64([0n, -400])?.[1], 0),
|
|
384
|
+
// Negatives mirror positives exactly, subnormals included.
|
|
385
|
+
() => assertBinary64([-3n, -1074])([-3n, -1074]),
|
|
386
|
+
() => assertBinary64([-1n, -1022])([-(1n << 52n), -1074]),
|
|
387
|
+
() => assertBinary64([-1n, 0])([-(1n << 52n), -52]),
|
|
388
|
+
// A whole-number input takes the other scaling branch (`de >= 0`),
|
|
389
|
+
// both when it needs widening and when it needs truncating.
|
|
390
|
+
() => assertBinary64([1n, 0])([1n << 52n, -52]),
|
|
391
|
+
() => assertBinary64([(1n << 60n) + 1n, 0])([1n << 52n, 8]),
|
|
392
|
+
],
|
|
393
|
+
// Cross-check against `Number(decimal)`, which the language specifies to
|
|
394
|
+
// be correctly rounded. 13220 decimals, every one of them agreeing.
|
|
395
|
+
binary64AgreesWithNumber: () => corpus.forEach(assertSameAsNumber),
|
|
264
396
|
}
|
|
@@ -4,3 +4,20 @@
|
|
|
4
4
|
* @module
|
|
5
5
|
*/
|
|
6
6
|
export type BigFloat = readonly [bigint, number];
|
|
7
|
+
/**
|
|
8
|
+
* A binary floating-point format: how many significant bits a value may carry,
|
|
9
|
+
* and the range its exponent may take.
|
|
10
|
+
*
|
|
11
|
+
* `minExp` and `maxExp` are in `BigFloat`'s own units — the exponent of the
|
|
12
|
+
* mantissa's last bit, not of its first. So `minExp` is the exponent of the
|
|
13
|
+
* smallest representable value, and `maxExp` the one the largest finite value
|
|
14
|
+
* carries with a full `precision`-bit mantissa. The other convention (the
|
|
15
|
+
* exponent of the *leading* bit, as IEEE-754 states it) differs by
|
|
16
|
+
* `precision - 1` on one end and `precision` on the other; keeping one set of
|
|
17
|
+
* units is why this type restates them rather than naming IEEE's.
|
|
18
|
+
*/
|
|
19
|
+
export type Format = {
|
|
20
|
+
readonly precision: number;
|
|
21
|
+
readonly minExp: number;
|
|
22
|
+
readonly maxExp: number;
|
|
23
|
+
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Function combinators: composition, identity,
|
|
3
|
-
* chainable `Fn` wrapper.
|
|
2
|
+
* Function combinators: composition, identity, sequential iteration, argument
|
|
3
|
+
* flipping, and the chainable `Fn` wrapper.
|
|
4
4
|
*
|
|
5
5
|
* @module
|
|
6
6
|
*
|
|
@@ -19,6 +19,15 @@ export declare const compose: <I, X>(g: Func<I, X>) => <O>(f: Func<X, O>) => Fun
|
|
|
19
19
|
* @type {<T>(value: T) => T}
|
|
20
20
|
*/
|
|
21
21
|
export declare const identity: <T>(value: T) => T;
|
|
22
|
+
/**
|
|
23
|
+
* Applies `f` to `value` `n` times sequentially.
|
|
24
|
+
*
|
|
25
|
+
* Unlike monoid `repeat`, this performs every application, taking O(n)
|
|
26
|
+
* applications. A non-positive `n` leaves `value` unchanged.
|
|
27
|
+
*
|
|
28
|
+
* @type {(n: bigint) => <T>(value: T) => (f: (value: T) => T) => T}
|
|
29
|
+
*/
|
|
30
|
+
export declare const iterate: (n: bigint) => <T>(value: T) => (f: (value: T) => T) => T;
|
|
22
31
|
/**
|
|
23
32
|
* Flips the arguments of a curried function.
|
|
24
33
|
*
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Function combinators: composition, identity,
|
|
3
|
-
* chainable `Fn` wrapper.
|
|
2
|
+
* Function combinators: composition, identity, sequential iteration, argument
|
|
3
|
+
* flipping, and the chainable `Fn` wrapper.
|
|
4
4
|
*
|
|
5
5
|
* @module
|
|
6
6
|
*
|
|
@@ -21,6 +21,24 @@ export const compose = g => f => x => f(g(x))
|
|
|
21
21
|
*/
|
|
22
22
|
export const identity = value => value
|
|
23
23
|
|
|
24
|
+
/**
|
|
25
|
+
* Applies `f` to `value` `n` times sequentially.
|
|
26
|
+
*
|
|
27
|
+
* Unlike monoid `repeat`, this performs every application, taking O(n)
|
|
28
|
+
* applications. A non-positive `n` leaves `value` unchanged.
|
|
29
|
+
*
|
|
30
|
+
* @type {(n: bigint) => <T>(value: T) => (f: (value: T) => T) => T}
|
|
31
|
+
*/
|
|
32
|
+
export const iterate = n => value => f => {
|
|
33
|
+
let v = value
|
|
34
|
+
let i = 0n
|
|
35
|
+
while (i < n) {
|
|
36
|
+
v = f(v)
|
|
37
|
+
i = i + 1n
|
|
38
|
+
}
|
|
39
|
+
return v
|
|
40
|
+
}
|
|
41
|
+
|
|
24
42
|
/**
|
|
25
43
|
* Flips the arguments of a curried function.
|
|
26
44
|
*
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { fn } from './module.f.mjs'
|
|
1
|
+
import { fn, iterate } from './module.f.mjs'
|
|
2
2
|
import { assertEq } from '../../asserts/module.f.mjs'
|
|
3
3
|
|
|
4
4
|
export const proof = () => {
|
|
@@ -13,4 +13,8 @@ export const proof = () => {
|
|
|
13
13
|
|
|
14
14
|
const result = r('hello')
|
|
15
15
|
assertEq(result, 5, r)
|
|
16
|
+
|
|
17
|
+
assertEq(iterate(0n)(5)(x => x + 1), 5, iterate)
|
|
18
|
+
assertEq(iterate(3n)(5)(x => x + 1), 8, iterate)
|
|
19
|
+
assertEq(iterate(-1n)(5)(x => x + 1), 5, iterate)
|
|
16
20
|
}
|
|
@@ -16,6 +16,27 @@ export type { phantomKey };
|
|
|
16
16
|
* The field is optional (`?`) so it never needs to be present at runtime.
|
|
17
17
|
* Use `phantomKey` to read the phantom type back out via a conditional type:
|
|
18
18
|
* `S extends Phantom<unknown, infer T> ? T : never`.
|
|
19
|
+
*
|
|
20
|
+
* **`T` is an unchecked annotation, not a derivation** — nothing stops it from
|
|
21
|
+
* being wrong, and once something reads it back (e.g. `Ts<>` in
|
|
22
|
+
* `fjs/types/rtti/ts/types.ts`, which short-circuits to `T` instead of
|
|
23
|
+
* structurally recursing), a wrong `T` is trusted silently. Guard every
|
|
24
|
+
* `Phantom<typeof rawThunk, T>` with two asserts: one against the
|
|
25
|
+
* un-annotated `rawThunk` (forces the real structural check, catching a
|
|
26
|
+
* wrong `T`) and one against the phantom-wrapped export (catches the export
|
|
27
|
+
* and the raw thunk drifting apart), using `Check` from
|
|
28
|
+
* `fjs/types/rtti/ts/types.ts` — or `Check3`, which pairs the two into one
|
|
29
|
+
* assert. See `fjs/edag/module.f.mjs` (`_exp`/`exp`) for the pattern:
|
|
30
|
+
*
|
|
31
|
+
* ```ts
|
|
32
|
+
* const rawThunk = () => [...] as const
|
|
33
|
+
* export const thunk: Phantom<typeof rawThunk, MyType> = rawThunk
|
|
34
|
+
* type _Check = Assert<Check3<MyType, typeof rawThunk, typeof thunk>>
|
|
35
|
+
* ```
|
|
36
|
+
*
|
|
37
|
+
* One phantom per recursive cycle is enough: `fjs/edag` wraps only `exp`,
|
|
38
|
+
* the union every node kind recurses through, and the node schemas
|
|
39
|
+
* themselves stay un-phantomed, each pinned with a plain `Check`.
|
|
19
40
|
*/
|
|
20
41
|
export type Phantom<S, T> = S & {
|
|
21
42
|
readonly [phantomKey]?: T;
|
|
@@ -27,19 +27,19 @@ import type { Ok, Error, Result } from './types.ts';
|
|
|
27
27
|
/**
|
|
28
28
|
* Creates a successful result.
|
|
29
29
|
*
|
|
30
|
-
* @template T
|
|
30
|
+
* @template const T
|
|
31
31
|
* @param {T} value - The value to wrap.
|
|
32
32
|
* @returns {Ok<T>} A successful result containing the value.
|
|
33
33
|
*/
|
|
34
|
-
export declare const ok: <T>(value: T) => Ok<T>;
|
|
34
|
+
export declare const ok: <const T>(value: T) => Ok<T>;
|
|
35
35
|
/**
|
|
36
36
|
* Creates a failed result.
|
|
37
37
|
*
|
|
38
|
-
* @template E
|
|
38
|
+
* @template const E
|
|
39
39
|
* @param {E} e - The error to wrap.
|
|
40
40
|
* @returns {Error<E>} A failed result containing the error.
|
|
41
41
|
*/
|
|
42
|
-
export declare const error: <E>(e: E) => Error<E>;
|
|
42
|
+
export declare const error: <const E>(e: E) => Error<E>;
|
|
43
43
|
/**
|
|
44
44
|
* Unwraps a result, returning the value if successful or throwing the error if failed.
|
|
45
45
|
*
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
/**
|
|
28
28
|
* Creates a successful result.
|
|
29
29
|
*
|
|
30
|
-
* @template T
|
|
30
|
+
* @template const T
|
|
31
31
|
* @param {T} value - The value to wrap.
|
|
32
32
|
* @returns {Ok<T>} A successful result containing the value.
|
|
33
33
|
*/
|
|
@@ -36,7 +36,7 @@ export const ok = value => ['ok', value]
|
|
|
36
36
|
/**
|
|
37
37
|
* Creates a failed result.
|
|
38
38
|
*
|
|
39
|
-
* @template E
|
|
39
|
+
* @template const E
|
|
40
40
|
* @param {E} e - The error to wrap.
|
|
41
41
|
* @returns {Error<E>} A failed result containing the error.
|
|
42
42
|
*/
|
|
@@ -1,17 +1,21 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @import { Result } from './types.ts'
|
|
2
|
+
* @import { Error, Ok, Result } from './types.ts'
|
|
3
|
+
* @import { Assert } from '../../asserts/types.ts'
|
|
4
|
+
* @import { Equal } from '../ts/types.ts'
|
|
3
5
|
*/
|
|
4
6
|
declare const example: () => void;
|
|
7
|
+
declare const constInference: () => void;
|
|
5
8
|
declare const invertTest: () => void;
|
|
6
9
|
declare const mapOkTest: () => void;
|
|
7
10
|
declare const okThenTest: () => void;
|
|
8
11
|
export declare const proof: {
|
|
9
12
|
example: typeof example;
|
|
13
|
+
constInference: typeof constInference;
|
|
10
14
|
invertTest: typeof invertTest;
|
|
11
15
|
mapOkTest: typeof mapOkTest;
|
|
12
16
|
okThenTest: typeof okThenTest;
|
|
13
17
|
throw: {
|
|
14
|
-
unwrapError: () =>
|
|
18
|
+
unwrapError: () => "oops";
|
|
15
19
|
};
|
|
16
20
|
};
|
|
17
21
|
export {};
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @import { Result } from './types.ts'
|
|
2
|
+
* @import { Error, Ok, Result } from './types.ts'
|
|
3
|
+
* @import { Assert } from '../../asserts/types.ts'
|
|
4
|
+
* @import { Equal } from '../ts/types.ts'
|
|
3
5
|
*/
|
|
4
6
|
|
|
5
7
|
import { error, ok, unwrap, invert, mapOk, okThen } from './module.f.mjs'
|
|
@@ -18,6 +20,19 @@ const example = () => {
|
|
|
18
20
|
assertEq(v, 'Something went wrong', 'error')
|
|
19
21
|
}
|
|
20
22
|
|
|
23
|
+
// `ok` and `error` take `const` type parameters, so a literal argument keeps
|
|
24
|
+
// its literal type without an `@type {const}` cast at the call site — a tagged
|
|
25
|
+
// error stays a tuple of literals instead of widening to `string[]`. These two
|
|
26
|
+
// assertions are what fail if a modifier is dropped.
|
|
27
|
+
const constInference = () => {
|
|
28
|
+
const o = ok([1, 2])
|
|
29
|
+
/** @typedef {Assert<Equal<typeof o, Ok<readonly [1, 2]>>>} _ConstOk */
|
|
30
|
+
const e = error(['notImplemented', 'read'])
|
|
31
|
+
/** @typedef {Assert<Equal<typeof e, Error<readonly ['notImplemented', 'read']>>>} _ConstError */
|
|
32
|
+
assertEq(o[1][1], 2)
|
|
33
|
+
assertEq(e[1][0], 'notImplemented')
|
|
34
|
+
}
|
|
35
|
+
|
|
21
36
|
const invertTest = () => {
|
|
22
37
|
const [k0, v0] = invert(ok(42))
|
|
23
38
|
assert(!(k0 !== 'error' || v0 !== 42), [k0, v0])
|
|
@@ -52,6 +67,7 @@ const okThenTest = () => {
|
|
|
52
67
|
|
|
53
68
|
export const proof = {
|
|
54
69
|
example,
|
|
70
|
+
constInference,
|
|
55
71
|
invertTest,
|
|
56
72
|
mapOkTest,
|
|
57
73
|
okThenTest,
|