diff --git a/AGENTS.md b/AGENTS.md index 4369f52b4..6702582a4 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -180,7 +180,7 @@ source dependency-closure rule as any other migrated file: its relative runtime imports and JSDoc type references may target `.f.mjs` modules only. This is a JavaScript/JSDoc and dependency-readiness rule, not a FunctionalScript compiler feature gate. A proof that still imports an unmigrated helper such as -`fjs/asserts/module.f.ts` remains `proof.f.ts` until that helper migrates. See +`fjs/types/list/module.f.ts` remains `proof.f.ts` until that helper migrates. See [`fjs/fsc/README.md`](./fjs/fsc/README.md) for the migration order and the module-import policy it implies. @@ -188,8 +188,8 @@ module-import policy it implies. Assert results in `proof` code with `assert`/`assertEq` from the current authored `fjs/asserts/module.f.*` source, not a hand-written `if (cond) { throw ... }`. -During Stage 1 that helper is `.f.ts` until it migrates to `.f.mjs`; a -`proof.f.mjs` that needs it must wait for that dependency migration. +That helper has already migrated, so the current source is +`fjs/asserts/module.f.mjs` and a `proof.f.mjs` may import it directly. A local `if`/`throw` in a test is itself a new branch for the coverage tool to track, and its failure side is normally never exercised (the test is expected to @@ -202,7 +202,7 @@ new uncovered branch. To prove that a type resolves to what you claim, write `type _Name = Assert>` — `Assert` from -`fjs/asserts/module.f.ts`, `Equal` from `fjs/types/ts/module.f.ts`. A wrong +`fjs/asserts/module.f.mjs`, `Equal` from `fjs/types/ts/module.f.mjs`. A wrong claim is then a compile error (TS2344, "Type 'false' does not satisfy the constraint 'true'"), and the check costs nothing at runtime. diff --git a/CHANGELOG.md b/CHANGELOG.md index 3de0bfb0e..d5b87cdc3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,11 @@ history. ## Unreleased +- **BREAKING CHANGES:** `fjs/types/function/compare` and + `fjs/types/function/operator` migrate from authored TypeScript (`.f.ts`) to + JSDoc-typed JavaScript (`.f.mjs`) under the stage-1 TypeScript-to-mjs + migration — importers must use the `.f.mjs` specifier + [#1456](https://github.com/functionalscript/functionalscript/pull/1456) - **BREAKING CHANGES:** `fjs/types/array`, `fjs/types/function`, `fjs/types/nullable`, and `fjs/types/option` migrate from authored TypeScript (`.f.ts`) to JSDoc-typed JavaScript (`.f.mjs`) under the stage-1 diff --git a/fjs/asn.1/module.f.ts b/fjs/asn.1/module.f.ts index 21461134e..caf0c7134 100644 --- a/fjs/asn.1/module.f.ts +++ b/fjs/asn.1/module.f.ts @@ -18,7 +18,7 @@ import { type Vec } from "../types/bit_vec/module.f.ts" import { identity } from "../types/function/module.f.mjs" -import { max } from "../types/function/compare/module.f.ts" +import { max } from "../types/function/compare/module.f.mjs" import { encode as b128encode, decode as b128decode } from "../basen/base128/module.f.ts" const { popFront: pop, listToVec } = msb diff --git a/fjs/bnf/ll1/module.f.ts b/fjs/bnf/ll1/module.f.ts index 4e57853de..0d2035f87 100644 --- a/fjs/bnf/ll1/module.f.ts +++ b/fjs/bnf/ll1/module.f.ts @@ -11,7 +11,7 @@ * @module */ import { type CodePoint } from '../../text/utf16/module.f.ts' -import { strictEqual } from '../../types/function/operator/module.f.ts' +import { strictEqual } from '../../types/function/operator/module.f.mjs' import { toArray } from '../../types/list/module.f.ts' import { rangeMap, type RangeMapArray } from '../../types/range_map/module.f.ts' import { contains, set, type StringSet } from '../../types/string_set/module.f.ts' diff --git a/fjs/common/monoid/module.f.ts b/fjs/common/monoid/module.f.ts index b8dae149a..b1dd12662 100644 --- a/fjs/common/monoid/module.f.ts +++ b/fjs/common/monoid/module.f.ts @@ -6,7 +6,7 @@ * * @module */ -import type { Fold, Reduce } from '../../types/function/operator/module.f.ts' +import type { Fold, Reduce } from '../../types/function/operator/module.f.mjs' import { reduce, type List } from '../../types/list/module.f.ts' import { flip } from '../../types/function/module.f.mjs' diff --git a/fjs/crypto/secp/module.f.ts b/fjs/crypto/secp/module.f.ts index 391af8185..c2f370035 100644 --- a/fjs/crypto/secp/module.f.ts +++ b/fjs/crypto/secp/module.f.ts @@ -5,7 +5,7 @@ * * @module */ -import type { Equal, Fold, Reduce } from '../../types/function/operator/module.f.ts' +import type { Equal, Fold, Reduce } from '../../types/function/operator/module.f.mjs' import { prime_field, sqrt, type PrimeField } from '../../types/prime_field/module.f.ts' import { repeat } from '../../common/monoid/module.f.ts' diff --git a/fjs/crypto/sha2/module.f.ts b/fjs/crypto/sha2/module.f.ts index 28a42f681..ef6ffd4da 100644 --- a/fjs/crypto/sha2/module.f.ts +++ b/fjs/crypto/sha2/module.f.ts @@ -14,7 +14,7 @@ import { uint, type Vec } from '../../types/bit_vec/module.f.ts' -import type { Fold } from '../../types/function/operator/module.f.ts' +import type { Fold } from '../../types/function/operator/module.f.mjs' import { fold, type List } from '../../types/list/module.f.ts' const { concat, front } = msb diff --git a/fjs/djs/parser/module.f.ts b/fjs/djs/parser/module.f.ts index 6921664ba..d702d5d59 100644 --- a/fjs/djs/parser/module.f.ts +++ b/fjs/djs/parser/module.f.ts @@ -5,7 +5,7 @@ */ import { error, ok, type Result } from '../../types/result/module.f.ts' import { fold, next, toArray, length, concat, type List } from '../../types/list/module.f.ts' -import type { Fold } from '../../types/function/operator/module.f.ts' +import type { Fold } from '../../types/function/operator/module.f.mjs' import type { DjsToken, DjsTokenWithMetadata } from '../tokenizer/module.f.ts' import { setReplace, at, type OrderedMap } from '../../types/ordered_map/module.f.ts' import { fromMap } from '../../types/object/module.f.ts' diff --git a/fjs/djs/serializer/module.f.ts b/fjs/djs/serializer/module.f.ts index e20eb9f7b..41fe45a3a 100644 --- a/fjs/djs/serializer/module.f.ts +++ b/fjs/djs/serializer/module.f.ts @@ -4,7 +4,7 @@ * @module */ import type { Unknown, Object } from '../module.f.ts' -import type { Fold } from '../../types/function/operator/module.f.ts' +import type { Fold } from '../../types/function/operator/module.f.mjs' import type { Entry as ObjectEntry } from '../../types/object/module.f.ts' import { fold } from '../../types/list/module.f.ts' import { concat } from '../../types/string/module.f.ts' diff --git a/fjs/djs/tokenizer/module.f.ts b/fjs/djs/tokenizer/module.f.ts index bc5ef521e..6b6e0cbfd 100644 --- a/fjs/djs/tokenizer/module.f.ts +++ b/fjs/djs/tokenizer/module.f.ts @@ -53,7 +53,7 @@ import { range as asciiRange, } from "../../text/ascii/module.f.ts" import { type CodePoint, codePointListToString, stringToCodePointList } from "../../text/utf16/module.f.ts" -import type { StateScan } from "../../types/function/operator/module.f.ts" +import type { StateScan } from "../../types/function/operator/module.f.mjs" import { contains } from "../../types/range/module.f.ts" import { concat, empty, filter, flat, flatMap, fold, map, stateScan, toArray, type List } from "../../types/list/module.f.ts" import { stringifyAsTree } from "../serializer/module.f.ts" diff --git a/fjs/emergent_testing/todo/f-mjs-test-and-coverage.md b/fjs/emergent_testing/todo/f-mjs-test-and-coverage.md index 6b69e6051..3197475a8 100644 --- a/fjs/emergent_testing/todo/f-mjs-test-and-coverage.md +++ b/fjs/emergent_testing/todo/f-mjs-test-and-coverage.md @@ -31,7 +31,7 @@ may migrate from `proof.f.ts` to `proof.f.mjs` as soon as it can be expressed as JavaScript with JSDoc and its authored runtime and declaration-retained type dependencies are already `.f.mjs`; current FunctionalScript compiler support is not a migration gate. A proof that still depends on an unmigrated `.f.ts` helper, -such as `fjs/asserts/module.f.ts`, remains `proof.f.ts` until that dependency +such as `fjs/types/list/module.f.ts`, remains `proof.f.ts` until that dependency moves. ### Proposal diff --git a/fjs/fsc/module.f.ts b/fjs/fsc/module.f.ts index 6353ec011..4c75b753b 100644 --- a/fjs/fsc/module.f.ts +++ b/fjs/fsc/module.f.ts @@ -3,7 +3,7 @@ * * @module */ -import { strictEqual } from '../types/function/operator/module.f.ts' +import { strictEqual } from '../types/function/operator/module.f.mjs' import { merge as rangeMapMerge, fromRange, diff --git a/fjs/fsm/module.f.ts b/fjs/fsm/module.f.ts index fcf0e47c0..7f6f88321 100644 --- a/fjs/fsm/module.f.ts +++ b/fjs/fsm/module.f.ts @@ -15,7 +15,7 @@ import { type RangeMapArray, type Entry } from '../types/range_map/module.f.ts' -import { type Fold, type Scan, strictEqual } from '../types/function/operator/module.f.ts' +import { type Fold, type Scan, strictEqual } from '../types/function/operator/module.f.mjs' import { stringify } from '../media/json/module.f.ts' import { identity } from '../types/function/module.f.mjs' import { stringToList } from '../text/utf16/module.f.ts' diff --git a/fjs/js/tokenizer/module.f.ts b/fjs/js/tokenizer/module.f.ts index f36522d08..6b5bc579a 100644 --- a/fjs/js/tokenizer/module.f.ts +++ b/fjs/js/tokenizer/module.f.ts @@ -5,7 +5,7 @@ * * @module */ -import { strictEqual, type Scan, type StateScan } from '../../types/function/operator/module.f.ts' +import { strictEqual, type Scan, type StateScan } from '../../types/function/operator/module.f.mjs' import { merge, fromRange, get, type RangeMapArray, type RangeMerge } from '../../types/range_map/module.f.ts' import { empty, stateScan, flat, toArray, reduce as listReduce, scan, map as listMap, type List } from '../../types/list/module.f.ts' import { at, fromEntries, type Entry } from '../../types/ordered_map/module.f.ts' diff --git a/fjs/media/json/parser/module.f.ts b/fjs/media/json/parser/module.f.ts index 5ecc47fc9..a5f21f9f9 100644 --- a/fjs/media/json/parser/module.f.ts +++ b/fjs/media/json/parser/module.f.ts @@ -5,7 +5,7 @@ */ import { error, ok, type Result } from '../../../types/result/module.f.ts' import { type List, fold, next, toArray, concat } from '../../../types/list/module.f.ts' -import { type Fold } from '../../../types/function/operator/module.f.ts' +import { type Fold } from '../../../types/function/operator/module.f.mjs' import { type JsonToken } from '../tokenizer/module.f.ts' import { setReplace, type OrderedMap } from '../../../types/ordered_map/module.f.ts' import { type Unknown } from '../module.f.ts' diff --git a/fjs/media/json/serializer/module.f.ts b/fjs/media/json/serializer/module.f.ts index 52ddd7f9a..ec01a9ed1 100644 --- a/fjs/media/json/serializer/module.f.ts +++ b/fjs/media/json/serializer/module.f.ts @@ -8,7 +8,7 @@ * @module */ import { flat, map, reduce, empty, type List } from '../../../types/list/module.f.ts' -import { type Reduce } from '../../../types/function/operator/module.f.ts' +import { type Reduce } from '../../../types/function/operator/module.f.mjs' import { concat } from '../../../types/string/module.f.ts' import { codePointToString, diff --git a/fjs/media/json/tokenizer/module.f.ts b/fjs/media/json/tokenizer/module.f.ts index 1a9180928..04d75c88e 100644 --- a/fjs/media/json/tokenizer/module.f.ts +++ b/fjs/media/json/tokenizer/module.f.ts @@ -3,7 +3,7 @@ * * @module */ -import { type StateScan } from '../../../types/function/operator/module.f.ts' +import { type StateScan } from '../../../types/function/operator/module.f.mjs' import { concat, empty, flat, stateScan, type List } from '../../../types/list/module.f.ts' import { multiply } from '../../../types/bigfloat/module.f.ts' import { diff --git a/fjs/path/module.f.ts b/fjs/path/module.f.ts index f446cc9b7..79ab843df 100644 --- a/fjs/path/module.f.ts +++ b/fjs/path/module.f.ts @@ -3,7 +3,7 @@ * * @module */ -import type { Fold, Reduce, Unary } from '../types/function/operator/module.f.ts' +import type { Fold, Reduce, Unary } from '../types/function/operator/module.f.mjs' import { type List, fold, last, take, length, concat as listConcat, toArray } from '../types/list/module.f.ts' import { join as listJoin, concat as stringConcat } from '../types/string/module.f.ts' diff --git a/fjs/sul/level/literal/module.f.ts b/fjs/sul/level/literal/module.f.ts index 94a2489e8..2b67ed33e 100644 --- a/fjs/sul/level/literal/module.f.ts +++ b/fjs/sul/level/literal/module.f.ts @@ -8,7 +8,7 @@ import { log2 } from '../../../types/bigint/module.f.ts' import { msb, vec, type Vec } from '../../../types/bit_vec/module.f.ts' import type { Func } from '../../../types/function/module.f.mjs' -import { strictEqual, type Equal, type StateScan } from '../../../types/function/operator/module.f.ts' +import { strictEqual, type Equal, type StateScan } from '../../../types/function/operator/module.f.mjs' import { equal, map, type List } from '../../../types/list/module.f.ts' import { join } from '../../../types/string/module.f.ts' diff --git a/fjs/text/code_point/module.f.ts b/fjs/text/code_point/module.f.ts index e25da148e..b31aefca4 100644 --- a/fjs/text/code_point/module.f.ts +++ b/fjs/text/code_point/module.f.ts @@ -9,7 +9,7 @@ * @module */ import { flat, type List, stateScan } from '../../types/list/module.f.ts' -import type { StateScan } from '../../types/function/operator/module.f.ts' +import type { StateScan } from '../../types/function/operator/module.f.mjs' import { contains } from '../../types/range/module.f.ts' /** diff --git a/fjs/text/utf16/module.f.ts b/fjs/text/utf16/module.f.ts index d7337519c..98fec3ed1 100644 --- a/fjs/text/utf16/module.f.ts +++ b/fjs/text/utf16/module.f.ts @@ -14,7 +14,7 @@ import { type Result, type Thunk, } from '../../types/list/module.f.ts' -import { concat, type StateScan } from '../../types/function/operator/module.f.ts' +import { concat, type StateScan } from '../../types/function/operator/module.f.mjs' import { contains } from '../../types/range/module.f.ts' import { fn } from '../../types/function/module.f.mjs' import { diff --git a/fjs/text/utf8/module.f.ts b/fjs/text/utf8/module.f.ts index 160cfa729..8272803a8 100644 --- a/fjs/text/utf8/module.f.ts +++ b/fjs/text/utf8/module.f.ts @@ -4,7 +4,7 @@ * @module */ import { flatMap, toArray, type List, type Thunk } from '../../types/list/module.f.ts' -import type { StateScan } from '../../types/function/operator/module.f.ts' +import type { StateScan } from '../../types/function/operator/module.f.mjs' import type { Tuple } from '../../types/array/module.f.mjs' import { decoder, errorMask, isValidCodePoint } from '../code_point/module.f.ts' import { msb, u8List, length, type Vec } from '../../types/bit_vec/module.f.ts' diff --git a/fjs/types/bigint/module.f.ts b/fjs/types/bigint/module.f.ts index a6ca383f7..aac09a3b1 100644 --- a/fjs/types/bigint/module.f.ts +++ b/fjs/types/bigint/module.f.ts @@ -17,8 +17,8 @@ * ``` */ -import { cmp, type Sign } from '../function/compare/module.f.ts' -import type { Unary as OpUnary, Reduce as OpReduce } from '../function/operator/module.f.ts' +import { cmp, type Sign } from '../function/compare/module.f.mjs' +import type { Unary as OpUnary, Reduce as OpReduce } from '../function/operator/module.f.mjs' import { type List } from '../list/module.f.ts' import { fold } from '../../common/monoid/module.f.ts' diff --git a/fjs/types/bigint/proof.f.ts b/fjs/types/bigint/proof.f.ts index d99cb9bd9..df60e7b37 100644 --- a/fjs/types/bigint/proof.f.ts +++ b/fjs/types/bigint/proof.f.ts @@ -16,7 +16,7 @@ import { roundUp8 } from './module.f.ts' import { assert, assertEq } from '../../asserts/module.f.mjs' -import { min } from '../function/compare/module.f.ts' +import { min } from '../function/compare/module.f.mjs' const oldLog2 = (v: bigint): bigint => { if (v <= 0n) { return -1n } diff --git a/fjs/types/bit_vec/module.f.ts b/fjs/types/bit_vec/module.f.ts index 1773e9215..eec23f9c3 100644 --- a/fjs/types/bit_vec/module.f.ts +++ b/fjs/types/bit_vec/module.f.ts @@ -23,11 +23,11 @@ */ import { bitLength, divUp, mask, maxLength, xor, type Reduce as BigintReduce } from '../bigint/module.f.ts' import { flip, identity } from '../function/module.f.mjs' -import type { Binary, Fold, Reduce as OpReduce } from '../function/operator/module.f.ts' +import type { Binary, Fold, Reduce as OpReduce } from '../function/operator/module.f.mjs' import { map, tryFold, type Accumulator, type List, type Thunk } from '../list/module.f.ts' import { asBase, asNominal, type Nominal } from '../nominal/module.f.ts' import { repeat as mRepeat } from '../../common/monoid/module.f.ts' -import { cmp, max, min, type Sign } from '../function/compare/module.f.ts' +import { cmp, max, min, type Sign } from '../function/compare/module.f.mjs' import { mapUnwrap, type Nullable } from '../nullable/module.f.mjs' /** diff --git a/fjs/types/bit_vec/proof.f.ts b/fjs/types/bit_vec/proof.f.ts index 3d81fa69b..89e8c420e 100644 --- a/fjs/types/bit_vec/proof.f.ts +++ b/fjs/types/bit_vec/proof.f.ts @@ -1,6 +1,6 @@ import { assert, assertEq } from '../../asserts/module.f.mjs' import { mask } from '../bigint/module.f.ts' -import type { Sign } from '../function/compare/module.f.ts' +import type { Sign } from '../function/compare/module.f.mjs' import { asBase, asNominal } from '../nominal/module.f.ts' import { length, empty, uint, type Vec, vec, lsb, msb, type BitOrder, repeat, vec8, maxLength, u8ListToVec, tryU8ListToVec, u8List, chunkList, fromSentinel } from './module.f.ts' import { repeat as listRepeat, toArray, type List } from '../list/module.f.ts' diff --git a/fjs/types/btree/find/module.f.ts b/fjs/types/btree/find/module.f.ts index 7785fcb63..432cc9bd7 100644 --- a/fjs/types/btree/find/module.f.ts +++ b/fjs/types/btree/find/module.f.ts @@ -5,7 +5,7 @@ */ import type { Leaf1, Leaf2, Branch3, Branch5, TNode } from '../types/module.f.ts' import type { List } from '../../list/module.f.ts' -import { index3, index5, type Compare } from '../../function/compare/module.f.ts' +import { index3, index5, type Compare } from '../../function/compare/module.f.mjs' import type { KeyOf, Index, Tuple } from "../../array/module.f.mjs"; export type FirstLeaf1 = readonly[Index<3>, Leaf1] diff --git a/fjs/types/btree/remove/module.f.ts b/fjs/types/btree/remove/module.f.ts index aea043854..f785d9b1d 100644 --- a/fjs/types/btree/remove/module.f.ts +++ b/fjs/types/btree/remove/module.f.ts @@ -4,7 +4,7 @@ * @module */ import { collapseRoot, type Leaf1, type TNode, type Branch1, type Branch3, type Branch5, type Tree } from '../types/module.f.ts' -import type { Compare } from '../../function/compare/module.f.ts' +import type { Compare } from '../../function/compare/module.f.mjs' import { type Path, type PathItem, find } from '../find/module.f.ts' import { fold, concat, next } from '../../list/module.f.ts' import type { Tuple } from '../../array/module.f.mjs' diff --git a/fjs/types/btree/set/module.f.ts b/fjs/types/btree/set/module.f.ts index a577c1889..398fc3430 100644 --- a/fjs/types/btree/set/module.f.ts +++ b/fjs/types/btree/set/module.f.ts @@ -5,7 +5,7 @@ */ import { collapseRoot, type Branch1, type Branch3, type Branch5, type Branch7, type TNode, type Tree } from '../types/module.f.ts' import { find, type First, type PathItem, type Result } from '../find/module.f.ts' -import type { Compare } from '../../function/compare/module.f.ts' +import type { Compare } from '../../function/compare/module.f.mjs' import { fold } from '../../list/module.f.ts' type Branch1To3 = Branch1 | Branch3 diff --git a/fjs/types/function/compare/module.f.mjs b/fjs/types/function/compare/module.f.mjs new file mode 100644 index 000000000..2cab3d8ac --- /dev/null +++ b/fjs/types/function/compare/module.f.mjs @@ -0,0 +1,95 @@ +/** + * Comparison function types and helpers. + * + * @module + */ +/** @import { Index, Tuple } from '../../array/module.f.mjs' */ + +/** @typedef {-1 | 0 | 1} Sign */ + +/** + * @template T + * @typedef {(_: T) => Sign} Compare + */ + +/** + * @template T + * @typedef {(a: T) => Compare} Cmp + */ + +/** @type {(cmp: Compare) => (value: T) => Index<3>} */ +export const index3 + = cmp => value => /** @type {Index<3>} */(cmp(value) + 1) + +/** @type {(cmp: Compare) => (v2: Tuple<2, T>) => Index<5>} */ +export const index5 = cmp => ([v0, v1]) => { + const _0 = cmp(v0) + return /** @type {Index<5>} */(_0 <= 0 ? _0 + 1 : cmp(v1) + 3) +} + +/** @typedef {boolean | string | number | bigint} Cmp1 */ + +/** + * @template A + * @template B + * @typedef {[A, B] extends [boolean, boolean] ? boolean : + * [A, B] extends [string, string] ? string : + * [A, B] extends [number, number] ? number : + * [A, B] extends [bigint, bigint] ? bigint : + * never + * } Cmp2 + */ + +/** @type {(a: A) => >(b: B) => Sign} */ +export const cmp = a => b => + /** @type {any} */(a) < b ? -1 : /** @type {any} */(a) > b ? 1 : 0 + +/** + * Returns the smaller of two comparable values. The `Cmp2` constraint + * is the same one `cmp` uses: it rejects calls that mix incompatible primitive + * types (e.g. `min(1)("a")`) at compile time. + * + * @type {(a: A) => >(b: B) => A | B} + */ +export const min = a => b => cmp(a)(b) < 0 ? a : b + +/** + * Returns the larger of two comparable values. The `Cmp2` constraint + * is the same one `cmp` uses: it rejects calls that mix incompatible primitive + * types (e.g. `max(1)("a")`) at compile time. + * + * @type {(a: A) => >(b: B) => A | B} + */ +export const max = a => b => cmp(a)(b) > 0 ? a : b + +/** + * Binary search over `[0, len)`. `probe(mid)` returns the sign of the search + * key relative to the element at `mid` (`-1` before, `0` at, `1` after). On a + * hit it returns the matching index; on a miss it returns the converged lower + * bound `b` (the insertion point), which may equal `len`. + * + * `probe` must be monotonic over `[0, len)`: scanning indices left to right its + * result is non-increasing — a run of `1`s, then `0`s, then `-1`s. A + * non-monotonic probe yields an undefined position. + * + * @type {(len: number) => (probe: (mid: number) => Sign) => number} + */ +export const bsearch = len => probe => { + let b = 0 + let e = len - 1 + while (true) { + if (e < b) { return b } + const mid = b + (e - b >> 1) + switch (probe(mid)) { + case -1: { + e = mid - 1 + break + } + case 0: { return mid } + case 1: { + b = mid + 1 + break + } + } + } +} diff --git a/fjs/types/function/compare/module.f.ts b/fjs/types/function/compare/module.f.ts deleted file mode 100644 index 73dc5b532..000000000 --- a/fjs/types/function/compare/module.f.ts +++ /dev/null @@ -1,81 +0,0 @@ -/** - * Comparison function types and helpers. - * - * @module - */ -import type { Index, Tuple } from '../../array/module.f.mjs' - -export type Sign = -1|0|1 - -export type Compare = (_: T) => Sign - -export type Cmp = (a: T) => Compare - -export const index3: (cmp: Compare) => (value: T) => Index<3> - = cmp => value => (cmp(value) + 1) as Index<3> - -export const index5 - : (cmp: Compare) => (v2: Tuple<2, T>) => Index<5> - = cmp => ([v0, v1]) => { - const _0 = cmp(v0) - return (_0 <= 0 ? _0 + 1 : cmp(v1) + 3) as Index<5> - } - -export type Cmp1 = boolean | string | number | bigint - -export type Cmp2 = - [A, B] extends [boolean, boolean] ? boolean : - [A, B] extends [string, string] ? string : - [A, B] extends [number, number] ? number : - [A, B] extends [bigint, bigint] ? bigint : - never - -export const cmp = (a: A) => >(b: B): Sign => - a as any < b ? -1 : a as any > b ? 1 : 0 - -/** - * Returns the smaller of two comparable values. The `Cmp2` constraint - * is the same one `cmp` uses: it rejects calls that mix incompatible primitive - * types (e.g. `min(1)("a")`) at compile time. - */ -export const min = (a: A) => >(b: B): A | B => - cmp(a)(b) < 0 ? a : b - -/** - * Returns the larger of two comparable values. The `Cmp2` constraint - * is the same one `cmp` uses: it rejects calls that mix incompatible primitive - * types (e.g. `max(1)("a")`) at compile time. - */ -export const max = (a: A) => >(b: B): A | B => - cmp(a)(b) > 0 ? a : b - -/** - * Binary search over `[0, len)`. `probe(mid)` returns the sign of the search - * key relative to the element at `mid` (`-1` before, `0` at, `1` after). On a - * hit it returns the matching index; on a miss it returns the converged lower - * bound `b` (the insertion point), which may equal `len`. - * - * `probe` must be monotonic over `[0, len)`: scanning indices left to right its - * result is non-increasing — a run of `1`s, then `0`s, then `-1`s. A - * non-monotonic probe yields an undefined position. - */ -export const bsearch - = (len: number) => (probe: (mid: number) => Sign): number => { - let b = 0 - let e = len - 1 - while (true) { - if (e < b) { return b } - const mid = b + (e - b >> 1) - switch (probe(mid)) { - case -1: { - e = mid - 1 - break - } - case 0: { return mid } - case 1: { - b = mid + 1 - break - } - } - } - } diff --git a/fjs/types/function/compare/proof.f.ts b/fjs/types/function/compare/proof.f.ts index 184e617a3..d3687e061 100644 --- a/fjs/types/function/compare/proof.f.ts +++ b/fjs/types/function/compare/proof.f.ts @@ -1,4 +1,4 @@ -import { cmp, min, max } from './module.f.ts' +import { cmp, min, max } from './module.f.mjs' import { assertEq } from '../../../asserts/module.f.mjs' export const proof = () => { diff --git a/fjs/types/function/operator/module.f.ts b/fjs/types/function/operator/module.f.mjs similarity index 54% rename from fjs/types/function/operator/module.f.ts rename to fjs/types/function/operator/module.f.mjs index 6b027ed9f..695163443 100644 --- a/fjs/types/function/operator/module.f.ts +++ b/fjs/types/function/operator/module.f.mjs @@ -3,27 +3,54 @@ * * @module */ -export type Binary = (a: A) => (b: B) => R -export type Fold = Binary +/** + * @template A + * @template B + * @template R + * @typedef {(a: A) => (b: B) => R} Binary + */ + +/** + * @template I + * @template O + * @typedef {Binary} Fold + */ -export const join = (separator: string): Reduce => value => prior => +/** + * @template T + * @typedef {Fold} Reduce + */ + +/** @type {(separator: string) => Reduce} */ +export const join = separator => value => prior => `${prior}${separator}${value}` -export const concat: Reduce = i => acc => - `${acc}${i}` +/** @type {Reduce} */ +export const concat = i => acc => `${acc}${i}` -export type Unary = (value: T) => R +/** + * @template T + * @template R + * @typedef {(value: T) => R} Unary + */ -export const logicalNot: Unary - = v => !v +/** @type {Unary} */ +export const logicalNot = v => !v -export type Equal = Binary +/** + * @template T + * @typedef {Binary} Equal + */ -export const strictEqual = (a: T) => (b: T): boolean => - a === b +/** @type {(a: T) => (b: T) => boolean} */ +export const strictEqual = a => b => a === b -export type Scan = (input: I) => readonly[O, Scan] +/** + * @template I + * @template O + * @typedef {(input: I) => readonly[O, Scan]} Scan + */ /** * One step of a stream transducer: given an `input` symbol and the `prior` @@ -50,28 +77,39 @@ export type Scan = (input: I) => readonly[O, Scan] * A {@link Fold} is the output-less special case (state only); driving a * `StateScan` over a `List` is `stateScan` in `../../list/module.f.ts`, and * {@link stateScanToScan} hides the state to recover a {@link Scan}. + * + * @template I + * @template S + * @template O + * @typedef {(input: I, prior: S) => readonly[O, S]} StateScan */ -export type StateScan = (input: I, prior: S) => readonly[O, S] -export const stateScanToScan = (op: StateScan) => (prior: S): Scan => i => { +/** @type {(op: StateScan) => (prior: S) => Scan} */ +export const stateScanToScan = op => prior => i => { const [o, s] = op(i, prior) return [o, stateScanToScan(op)(s)] } -export const foldToScan = (fold: Fold) => (prior: O): Scan => i => { +/** @type {(fold: Fold) => (prior: O) => Scan} */ +export const foldToScan = fold => prior => i => { const result = fold(i)(prior) return [result, foldToScan(fold)(result)] } -export type Reduce = Fold - -export const reduceToScan = (op: Reduce): Scan => init => +/** @type {(op: Reduce) => Scan} */ +export const reduceToScan = op => init => [init, foldToScan(op)(init)] -export const addition: Reduce - = a => b => a + b +/** + * TODO: We should have one function for `number` | `bigint` and `string`. + * We can use the same approach as we use for comparing items, + * see `Cmp1` and `Cmp2` types. + * + * @type {Reduce} + */ +export const addition = a => b => a + b -export const increment: (b: number) => number - = addition(1) +/** @type {Unary} */ +export const increment = addition(1) export const counter = () => increment diff --git a/fjs/types/function/operator/proof.f.ts b/fjs/types/function/operator/proof.f.ts index 7494ae2be..339992757 100644 --- a/fjs/types/function/operator/proof.f.ts +++ b/fjs/types/function/operator/proof.f.ts @@ -10,7 +10,7 @@ import { stateScanToScan, foldToScan, reduceToScan, -} from './module.f.ts' +} from './module.f.mjs' const joinTest = () => { const result = join(', ')('world')('hello') diff --git a/fjs/types/function/todo/uncurry-accumulator-types.md b/fjs/types/function/todo/uncurry-accumulator-types.md index bc1c271c2..eaa1cff9a 100644 --- a/fjs/types/function/todo/uncurry-accumulator-types.md +++ b/fjs/types/function/todo/uncurry-accumulator-types.md @@ -10,7 +10,7 @@ Several sibling accumulator types still curry their data parameters, contradicting that precedent: ```ts -// fjs/types/function/operator/module.f.ts +// fjs/types/function/operator/module.f.mjs export type Fold = Binary // (input: I) => (acc: O) => O export type Reduce = Fold // (value: T) => (acc: T) => T diff --git a/fjs/types/list/module.f.ts b/fjs/types/list/module.f.ts index 064891a58..40b6ca481 100644 --- a/fjs/types/list/module.f.ts +++ b/fjs/types/list/module.f.ts @@ -17,7 +17,7 @@ import { type Fold, type Reduce, type Equal, -} from '../function/operator/module.f.ts' +} from '../function/operator/module.f.mjs' export type List = NotLazy | Thunk diff --git a/fjs/types/list/proof.f.ts b/fjs/types/list/proof.f.ts index d68f2c4da..8bb9c31d6 100644 --- a/fjs/types/list/proof.f.ts +++ b/fjs/types/list/proof.f.ts @@ -30,7 +30,7 @@ import { } from './module.f.ts' import { stringify, type Unknown } from '../../media/json/module.f.ts' import { sort } from '../object/module.f.ts' -import { addition, strictEqual, reduceToScan } from '../function/operator/module.f.ts' +import { addition, strictEqual, reduceToScan } from '../function/operator/module.f.mjs' import { assert, assertEq, assertNotNullish } from '../../asserts/module.f.mjs' const str diff --git a/fjs/types/number/module.f.ts b/fjs/types/number/module.f.ts index 2c6304a44..4bb2f1aaa 100644 --- a/fjs/types/number/module.f.ts +++ b/fjs/types/number/module.f.ts @@ -5,8 +5,8 @@ * @module */ import { reduce, type List } from '../list/module.f.ts' -import { addition } from '../function/operator/module.f.ts' -import { type Sign, cmp as uCmp, min as uMin, max as uMax } from '../function/compare/module.f.ts' +import { addition } from '../function/operator/module.f.mjs' +import { type Sign, cmp as uCmp, min as uMin, max as uMax } from '../function/compare/module.f.mjs' import { fold } from '../../common/monoid/module.f.ts' export const sum: (input: List) => number diff --git a/fjs/types/ordered_map/module.f.ts b/fjs/types/ordered_map/module.f.ts index 7ad336b58..76939ef9c 100644 --- a/fjs/types/ordered_map/module.f.ts +++ b/fjs/types/ordered_map/module.f.ts @@ -8,10 +8,10 @@ import { value, find } from '../btree/find/module.f.ts' import { set } from '../btree/set/module.f.ts' import { remove as btreeRemove } from '../btree/remove/module.f.ts' import { values } from '../btree/module.f.ts' -import type { Sign } from '../function/compare/module.f.ts' +import type { Sign } from '../function/compare/module.f.mjs' import { cmp } from '../string/module.f.ts' import { fold, type List } from '../list/module.f.ts' -import type { Reduce } from '../function/operator/module.f.ts' +import type { Reduce } from '../function/operator/module.f.mjs' export type Entry = readonly [string, T] diff --git a/fjs/types/range_map/module.f.ts b/fjs/types/range_map/module.f.ts index ac2d30921..0b19a202c 100644 --- a/fjs/types/range_map/module.f.ts +++ b/fjs/types/range_map/module.f.ts @@ -40,8 +40,8 @@ import { genericMerge, type TailReduce, type ReduceOp, type SortedList } from '. import { next } from '../list/module.f.ts' import type { Nullable } from '../nullable/module.f.mjs' import { cmp } from '../number/module.f.ts' -import { bsearch } from '../function/compare/module.f.ts' -import type { Reduce, Equal } from '../function/operator/module.f.ts' +import { bsearch } from '../function/compare/module.f.mjs' +import type { Reduce, Equal } from '../function/operator/module.f.mjs' import type { Range } from '../range/module.f.ts' export type Entry = [T, number] diff --git a/fjs/types/range_map/proof.f.ts b/fjs/types/range_map/proof.f.ts index 970ef6c09..95c5f09b2 100644 --- a/fjs/types/range_map/proof.f.ts +++ b/fjs/types/range_map/proof.f.ts @@ -3,7 +3,7 @@ import { stringify, type Unknown } from '../../media/json/module.f.ts' import { sort } from '../object/module.f.ts' import { union, type SortedSet } from '../sorted_set/module.f.ts' import { equal, toArray } from '../list/module.f.ts' -import { strictEqual } from '../function/operator/module.f.ts' +import { strictEqual } from '../function/operator/module.f.mjs' import { cmp } from '../string/module.f.ts' import { assertEq } from '../../asserts/module.f.mjs' diff --git a/fjs/types/sorted_list/module.f.ts b/fjs/types/sorted_list/module.f.ts index d7779680e..f780f5b78 100644 --- a/fjs/types/sorted_list/module.f.ts +++ b/fjs/types/sorted_list/module.f.ts @@ -3,7 +3,7 @@ * * @module */ -import { type Sign, type Cmp, bsearch } from '../function/compare/module.f.ts' +import { type Sign, type Cmp, bsearch } from '../function/compare/module.f.mjs' import { type List, next } from '../list/module.f.ts' import type { Nullable } from '../nullable/module.f.mjs' import { identity } from '../function/module.f.mjs' diff --git a/fjs/types/sorted_set/module.f.ts b/fjs/types/sorted_set/module.f.ts index bf9e3e33f..46e3d4083 100644 --- a/fjs/types/sorted_set/module.f.ts +++ b/fjs/types/sorted_set/module.f.ts @@ -26,7 +26,7 @@ * has(cmp)(2)(setA) // false * ``` */ -import type { Cmp } from '../function/compare/module.f.ts' +import type { Cmp } from '../function/compare/module.f.mjs' import { toArray } from "../list/module.f.ts" import { merge, intersect as listIntersect, find } from '../sorted_list/module.f.ts' diff --git a/fjs/types/string/module.f.ts b/fjs/types/string/module.f.ts index 112122f9f..dc6f0f4a3 100644 --- a/fjs/types/string/module.f.ts +++ b/fjs/types/string/module.f.ts @@ -17,8 +17,8 @@ */ import { type List, reduce as listReduce, repeat as listRepeat } from '../list/module.f.ts' import { compose } from '../function/module.f.mjs' -import { type Sign, cmp as uCmp } from '../function/compare/module.f.ts' -import { join as joinOp, type Reduce } from '../function/operator/module.f.ts' +import { type Sign, cmp as uCmp } from '../function/compare/module.f.mjs' +import { join as joinOp, type Reduce } from '../function/operator/module.f.mjs' import { fold } from '../../common/monoid/module.f.ts' // `join`'s per-separator reduction is seeded at `''` but is *not* a monoid fold: @@ -44,4 +44,4 @@ export const cmp: (a: string) => (b: string) => Sign = uCmp export const splitAt = (p: number) => (v: string): readonly[string, string] => - [v.substring(0, p), v.substring(p)] \ No newline at end of file + [v.substring(0, p), v.substring(p)] diff --git a/fjs/types/uint8array/proof.f.ts b/fjs/types/uint8array/proof.f.ts index e17aca76e..67853f3e3 100644 --- a/fjs/types/uint8array/proof.f.ts +++ b/fjs/types/uint8array/proof.f.ts @@ -1,6 +1,6 @@ import { maxLength, maxLengthBytes, vec } from '../bit_vec/module.f.ts' import { toVec, fromVec, listToVec, decodeUtf8, encodeUtf8 } from './module.f.ts' -import { strictEqual } from '../function/operator/module.f.ts' +import { strictEqual } from '../function/operator/module.f.mjs' import { equal, fromArrayLike } from '../list/module.f.ts' import { assert } from '../../asserts/module.f.mjs' diff --git a/todo/flow.md b/todo/flow.md index 57991a413..63be5e532 100644 --- a/todo/flow.md +++ b/todo/flow.md @@ -238,7 +238,7 @@ Planned engine work, each a separate change: - [fjs/types/list/module.f.ts](../fjs/types/list/module.f.ts) — the sequence type the naive engine binds to -- [fjs/types/function/operator/module.f.ts](../fjs/types/function/operator/module.f.ts) +- [fjs/types/function/operator/module.f.mjs](../fjs/types/function/operator/module.f.mjs) — `Scan`, `StateScan`, `Fold`: the closure-form operators `Transducer` generalizes (its JSDoc already frames them as Mealy machines) - [fjs/types/rtti](../fjs/types/rtti) — runtime type descriptions for the diff --git a/todo/migrate-typescript-to-mjs.md b/todo/migrate-typescript-to-mjs.md index a3170155b..5251aaf22 100644 --- a/todo/migrate-typescript-to-mjs.md +++ b/todo/migrate-typescript-to-mjs.md @@ -217,6 +217,11 @@ compiler-compatibility rename. dependencies on remaining authored TypeScript. - [ ] Update imports, proofs, tests, coverage globs, scripts, generated CI, and documentation for every migrated group. +- [ ] Sweep prose references to already-migrated modules: `AGENTS.md`, README + files, and `todo/*.md` still name `.f.ts` paths that no longer exist, so + snippets copied from them produce broken imports and links. Per-group + updates miss these because they are not importers; run a repository-wide + `.f.ts`-reference check at least at the end of stage 1. - [ ] Preserve Node, Deno, Bun, proof, coverage, type-checking, declaration, and CI package behavior throughout the migration. - [ ] Add required `**BREAKING CHANGES:**` changelog entries for public runtime