Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ history.

## Unreleased

- `types/sorted_list`: the two merge tail policies are now named `keepTail` and
`dropTail` instead of sharing one shadowed name; internal only
[#1546](https://github.com/functionalscript/functionalscript/pull/1546)
- `types/bit_vec`: `tryListToVec`/`tryU8ListToVec` reuse the shared balanced
fold, at the same cost as the accumulator they replace
[#1548](https://github.com/functionalscript/functionalscript/pull/1548)
Expand Down
2 changes: 1 addition & 1 deletion fjs/types/function/todo/uncurry-accumulator-types.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ This removes the partial-application footgun and drops one closure allocation pe

### Considerations

- **Broad mechanical refactor.** ~20+ operator definitions across `bigint`, `prime_field`, `bit_vec`, `string`, `monoid`, `number`, `range_map`, plus `operator` itself and the `foldToScan`/`reduceToScan`/`fold`/`reduce` plumbing in `list`. `genericMerge`/`cmpReduce`/`mergeTail` in `sorted_list` and the `range_map` merge consumers change accordingly.
- **Broad mechanical refactor.** ~20+ operator definitions across `bigint`, `prime_field`, `bit_vec`, `string`, `monoid`, `number`, `range_map`, plus `operator` itself and the `foldToScan`/`reduceToScan`/`fold`/`reduce` plumbing in `list`. `genericMerge`/`cmpReduce`/`keepTail` in `sorted_list` and the `range_map` merge consumers change accordingly.
- **`Fold` can no longer be `Binary<I, O, O>`.** This draws a clean line between combinators where currying is genuinely useful (`Binary`/`Equal`/`Unary`) and accumulators where currying is dangerous (`Fold`/`Reduce`/`StateScan`/`ReduceOp`/`TailReduce`).
- Could be split: `Fold`/`Reduce` first, `sorted_list`'s `ReduceOp`/`TailReduce` as a follow-up.

Expand Down
28 changes: 18 additions & 10 deletions fjs/types/sorted_list/module.f.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
*
* @import { Cmp } from '../function/compare/types.ts'
* @import { List } from '../list/types.ts'
* @import { ReduceOp, SortedList, TailReduce, _MergeReduce } from './types.ts'
* @import { ReduceOp, SortedList, _MergeReduce } from './types.ts'
*/

import { bsearch } from '../function/compare/module.f.mjs'
Expand Down Expand Up @@ -54,11 +54,7 @@ export const merge =
* @param {Cmp<T>} cmp
* @returns {(a: SortedList<T>) => (b: SortedList<T>) => SortedList<T>}
*/
cmp => {
/** @type {TailReduce<T, null>} */
const tailReduce = mergeTail
return genericMerge({ reduceOp: cmpReduce(cmp), tailReduce })(null)
}
cmp => genericMerge({ reduceOp: cmpReduce(cmp), tailReduce: keepTail })(null)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Add the required CHANGELOG entry

This commit changes executable fjs/types/sorted_list code, but the inspected CHANGELOG.md has no corresponding entry under ## Unreleased; add a concise entry linked to the real PR so this code change is included in the release notes.

AGENTS.md reference: AGENTS.md:L133-L135

Useful? React with 👍 / 👎.


const cmpReduce =
/**
Expand All @@ -71,10 +67,22 @@ const cmpReduce =
return [sign === 1 ? b : a, sign, null]
}

/** @type {() => <T>(tail: List<T>) => List<T>} */
const mergeTail = () => identity
/**
* The two tail policies `genericMerge` takes, named for what they do with the
* list that has not been exhausted yet: `merge` keeps it, because everything
* still there belongs in the union, and `intersect` drops it, because with one
* side exhausted nothing remaining can be matched.
*
* Both are left to inference deliberately. An explicit
* `() => <T>(tail: List<T>) => List<T>` compiles here but introduces its own
* type parameter, which `genericMerge` then collects as an inference candidate
* alongside `reduceOp`'s; the best common supertype of the two is `unknown`, so
* `T` widens and both call sites fail to match their declared return type. With
* no annotation there is no second candidate and `reduceOp` alone fixes `T`.
*/
const keepTail = () => identity

const tailReduce = () => () => null
const dropTail = () => () => null

const intersectReduce =
/**
Expand All @@ -93,7 +101,7 @@ export const intersect =
* @param {Cmp<T>} cmp
* @returns {(a: SortedList<T>) => (b: SortedList<T>) => SortedList<T>}
*/
cmp => genericMerge({ reduceOp: intersectReduce(cmp), tailReduce })(null)
cmp => genericMerge({ reduceOp: intersectReduce(cmp), tailReduce: dropTail })(null)

export const find =
/**
Expand Down
44 changes: 0 additions & 44 deletions fjs/types/sorted_list/todo/tail-reduce-shadowing.md

This file was deleted.

2 changes: 1 addition & 1 deletion todo/migrate-typescript-to-mjs.md
Original file line number Diff line number Diff line change
Expand Up @@ -558,7 +558,7 @@ data.

A related mechanical finding from the same review round: composing multiple
independently-generic helper functions inside another generic function's body
(e.g. `genericMerge` calling `cmpReduce` calling into `mergeTail`, all
(e.g. `genericMerge` calling `cmpReduce` calling into `keepTail`, all
separately `<T>`-generic) loses type inference when each is annotated with a
single `@type {<T, S>(...) => ...}` on the whole arrow chain — TypeScript
cannot always unify the type parameters across the nested generic-value calls,
Expand Down
Loading