Skip to content

Commit

Permalink
refactor(transducers): update Reducer, Transducer and other types
Browse files Browse the repository at this point in the history
BREAKING CHANGE: update generics in Reducer, Transducer and other types

- swap generic type args in `Reducer` and `IReducible` to be same order as in `Transducer`, `IXform`,
  i.e. `Reducer<A, B>` maps items from type A to type B, **not** the other way (as was)!
- this new order is more logical, less confusing and also in line with upcoming async transducers pkg
- changes in userland should be minimal (if any), only impacted are custom reducer definitions
- update all pkg internal occurrences
- various other small refactorings, type/arg updates, e.g.
  - add generics for `reduced()`/`ensureReduced()`/`unreduced()`
  - update `step()` return type (incl. undefined)
  - add `StructField` generics
  • Loading branch information
postspectacular committed Apr 3, 2024
1 parent 08e876f commit 95c43f3
Show file tree
Hide file tree
Showing 72 changed files with 245 additions and 245 deletions.
43 changes: 21 additions & 22 deletions packages/transducers/src/api.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { Comparator, Fn, Fn0, IObjectOf } from "@thi.ng/api";
import type { Reduced } from "./reduced.js";

export type Transducer<A, B> = (rfn: Reducer<any, B>) => Reducer<any, A>;
export type Transducer<A, B> = (rfn: Reducer<B, any>) => Reducer<A, any>;

/**
* A transducer or a custom type with a {@link IXform} implementation.
Expand All @@ -12,30 +12,29 @@ export type TxLike<A, B> = Transducer<A, B> | IXform<A, B>;
* Custom version of {@link TxLike} for use with {@link multiplex} and
* {@link multiplexObj}.
*/
export type MultiplexTxLike<T, A> = TxLike<T, A> | [TxLike<T, A>, boolean];
export type MultiplexTxLike<A, B> = TxLike<A, B> | [TxLike<A, B>, boolean];

export type ReductionFn<A, B> = (acc: A, x: B) => A | Reduced<A>;
/**
* Function which combines a new value of type `A` with accumulator of type `B`.
* If the reduction should terminate early, the function should wrap the result
* via {@link reduced}.
*/
export type ReductionFn<A, B> = (acc: B, x: A) => B | Reduced<B>;

/**
* A 3-tuple of functions defining the different stages of a reduction process.
*
* @remarks
* The items in order:
*
* 1. Initialization function used to produce an initial default result (only
* used if no such initial result was given by the user)
* 2. Completion function to post-process an already reduced result (for most
* reducers this is merely the identity function). Also see {@link reducer}.
* 3. Accumulation function, merging a new input value with the currently
* existing (partially) reduced result.
*/
export interface Reducer<A, B> extends Array<any> {
/**
* Initialization function to produce a default initial result (only used if
* no such initial result was given by the user)
*/
[0]: Fn0<A>;
/**
* Completion function to post-process an already reduced result (for most
* reducers this is merely the identity function). Also see {@link reducer}.
*/
[1]: Fn<A, A>;
/**
* Accumulation function, merging a new input value with the currently
* existing (partially) reduced result.
*/
[2]: ReductionFn<A, B>;
}
export type Reducer<A, B> = [Fn0<B>, Fn<B, B>, ReductionFn<A, B>];

/**
* Interface for types able to provide some internal functionality (or
Expand Down Expand Up @@ -99,7 +98,7 @@ export interface IReducible<A, B> {
* @param rfn
* @param acc
*/
$reduce(rfn: ReductionFn<A, B>, acc: A): A | Reduced<A>;
$reduce(rfn: ReductionFn<A, B>, acc: B | Reduced<B>): B | Reduced<B>;
}

export type TransformFn = (x: any) => any;
Expand All @@ -124,5 +123,5 @@ export interface SortOpts<A, B> {

export interface GroupByOpts<SRC, KEY, GROUP> {
key: Fn<SRC, KEY>;
group: Reducer<GROUP, SRC>;
group: Reducer<SRC, GROUP>;
}
6 changes: 3 additions & 3 deletions packages/transducers/src/assoc-map.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@ import { reduce, reducer } from "./reduce.js";
* Reducer accepting key-value pairs / tuples and transforming / adding
* them to an ES6 Map.
*/
export function assocMap<A, B>(): Reducer<Map<A, B>, Pair<A, B>>;
export function assocMap<A, B>(): Reducer<Pair<A, B>, Map<A, B>>;
export function assocMap<A, B>(xs: Iterable<Pair<A, B>>): Map<A, B>;
export function assocMap<A, B>(xs?: Iterable<Pair<A, B>>): any {
return xs
? reduce(assocMap(), xs)
: reducer<Map<A, B>, Pair<A, B>>(
() => new Map(),
: reducer<Pair<A, B>, Map<A, B>>(
() => new Map<A, B>(),
(acc, [k, v]) => acc.set(k, v)
);
}
4 changes: 2 additions & 2 deletions packages/transducers/src/assoc-obj.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@ import { reduce, reducer } from "./reduce.js";
* Reducer accepting key-value pairs / tuples and updating / adding them
* to an object.
*/
export function assocObj<T>(): Reducer<IObjectOf<T>, Pair<PropertyKey, T>>;
export function assocObj<T>(): Reducer<Pair<PropertyKey, T>, IObjectOf<T>>;
export function assocObj<T>(xs: Iterable<Pair<PropertyKey, T>>): IObjectOf<T>;
export function assocObj<T>(xs?: Iterable<Pair<PropertyKey, T>>): any {
return xs
? reduce(assocObj(), xs)
: reducer<IObjectOf<T>, Pair<PropertyKey, T>>(
: reducer<Pair<PropertyKey, T>, IObjectOf<T>>(
() => ({}),
(acc, [k, v]) => ((acc[<any>k] = v), acc)
);
Expand Down
4 changes: 2 additions & 2 deletions packages/transducers/src/auto-obj.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,13 @@ import { reduce, reducer } from "./reduce.js";
*
* @param prefix - shared prefix
*/
export function autoObj<T>(prefix: string): Reducer<IObjectOf<T>, T>;
export function autoObj<T>(prefix: string): Reducer<T, IObjectOf<T>>;
export function autoObj<T>(prefix: string, xs: Iterable<T>): IObjectOf<T>;
export function autoObj<T>(prefix: string, xs?: Iterable<T>): any {
let id = 0;
return xs
? reduce(autoObj(prefix), xs)
: reducer<IObjectOf<T>, T>(
: reducer<T, IObjectOf<T>>(
() => ({}),
(acc, v) => ((acc[prefix + id++] = v), acc)
);
Expand Down
2 changes: 1 addition & 1 deletion packages/transducers/src/benchmark.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export function benchmark(src: Iterable<any>): IterableIterator<number>;
export function benchmark(src?: Iterable<any>): any {
return isIterable(src)
? iterator1(benchmark(), src)
: (rfn: Reducer<any, number>) => {
: (rfn: Reducer<number, any>) => {
const r = rfn[2];
let prev = Date.now();
return compR(rfn, (acc, _) => {
Expand Down
2 changes: 1 addition & 1 deletion packages/transducers/src/cat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ import { ensureReduced, isReduced, unreduced } from "./reduced.js";
*/
export const cat =
<T>(): Transducer<Nullable<Iterable<T>>, T> =>
(rfn: Reducer<any, T>) => {
(rfn: Reducer<T, any>) => {
const r = rfn[2];
return compR(rfn, (acc, x: Iterable<T> | null | undefined) => {
if (x) {
Expand Down
2 changes: 1 addition & 1 deletion packages/transducers/src/compr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,6 @@ import type { Reducer, ReductionFn } from "./api.js";
* @param fn -
*/
export const compR = <A, B, C>(
rfn: Reducer<A, B>,
rfn: Reducer<B, C>,
fn: ReductionFn<A, C>
): Reducer<A, C> => [rfn[0], rfn[1], fn];
2 changes: 1 addition & 1 deletion packages/transducers/src/conj.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { reduce, reducer } from "./reduce.js";
/**
* Reducer. Like {@link push}, but for ES6 Sets.
*/
export function conj<T>(): Reducer<Set<T>, T>;
export function conj<T>(): Reducer<T, Set<T>>;
export function conj<T>(xs: Iterable<T>): Set<T>;
export function conj<T>(xs?: Iterable<T>): any {
return xs
Expand Down
2 changes: 1 addition & 1 deletion packages/transducers/src/converge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export function converge<T>(
export function converge<T>(...args: any[]): any {
return (
__iter(converge, args) ||
((rfn: Reducer<any, T>) => {
((rfn: Reducer<T, any>) => {
const r = rfn[2];
const pred = args[0];
let prev: any = SEMAPHORE;
Expand Down
5 changes: 2 additions & 3 deletions packages/transducers/src/count.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { $$reduce, reducer } from "./reduce.js";
* @param offset -
* @param step -
*/
export function count(offset?: number, step?: number): Reducer<number, any>;
export function count(offset?: number, step?: number): Reducer<any, number>;
export function count(xs: Iterable<any>): number;
export function count(offset: number, xs: Iterable<any>): number;
export function count(offset: number, step: number, xs: Iterable<any>): number;
Expand All @@ -17,8 +17,7 @@ export function count(...args: any[]): any {
if (res !== undefined) {
return res;
}
let offset: number = args[0] || 0;
let step: number = args[1] || 1;
const [offset = 0, step = 1] = <number[]>args;
return reducer(
() => offset,
(acc, _) => acc + step
Expand Down
2 changes: 1 addition & 1 deletion packages/transducers/src/dedupe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export function dedupe<T>(
export function dedupe<T>(...args: any[]): any {
return (
__iter(dedupe, args) ||
((rfn: Reducer<any, T>) => {
((rfn: Reducer<T, any>) => {
const r = rfn[2];
const equiv = args[0];
let prev: any = SEMAPHORE;
Expand Down
2 changes: 1 addition & 1 deletion packages/transducers/src/distinct.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export function distinct<T>(
export function distinct<T>(...args: any[]): any {
return (
__iter(distinct, args) ||
((rfn: Reducer<any, T>) => {
((rfn: Reducer<T, any>) => {
const r = rfn[2];
const opts = <DistinctOpts<T>>(args[0] || {});
const key = opts.key;
Expand Down
2 changes: 1 addition & 1 deletion packages/transducers/src/drop-while.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export function dropWhile<T>(
export function dropWhile<T>(...args: any[]): any {
return (
__iter(dropWhile, args) ||
((rfn: Reducer<any, T>) => {
((rfn: Reducer<T, any>) => {
const r = rfn[2];
const pred = args[0];
let ok = true;
Expand Down
2 changes: 1 addition & 1 deletion packages/transducers/src/drop.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export function drop<T>(n: number, src: Iterable<T>): IterableIterator<T>;
export function drop<T>(n: number, src?: Iterable<T>): any {
return isIterable(src)
? iterator1(drop(n), src)
: (rfn: Reducer<any, T>) => {
: (rfn: Reducer<T, any>) => {
const r = rfn[2];
let m = n;
return compR(rfn, (acc, x: T) =>
Expand Down
2 changes: 1 addition & 1 deletion packages/transducers/src/duplicate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export function duplicate<T>(n: number, src: Iterable<T>): IterableIterator<T>;
export function duplicate<T>(n = 1, src?: Iterable<T>): any {
return isIterable(src)
? iterator(duplicate(n), src)
: (rfn: Reducer<any, T>) => {
: (rfn: Reducer<T, any>) => {
const r = rfn[2];
return compR(rfn, (acc, x: T) => {
for (let i = n; i >= 0 && !isReduced(acc); i--) {
Expand Down
6 changes: 3 additions & 3 deletions packages/transducers/src/fill.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { $$reduce, reducer } from "./reduce.js";
*
* @param start -
*/
export function fill<T>(start?: number): Reducer<T[], T>;
export function fill<T>(start?: number): Reducer<T, T[]>;
export function fill<T>(xs: Iterable<T>): T[];
export function fill<T>(start: number, xs: Iterable<T>): T[];
export function fill<T>(...args: any[]): any {
Expand All @@ -18,7 +18,7 @@ export function fill<T>(...args: any[]): any {
return res;
}
let start = args[0] || 0;
return reducer<T[], T>(
return reducer<T, T[]>(
() => [],
(acc, x) => ((acc[start++] = x), acc)
);
Expand All @@ -29,7 +29,7 @@ export function fill<T>(...args: any[]): any {
*
* @param start -
*/
export function fillN(start?: number): Reducer<NumericArray, number>;
export function fillN(start?: number): Reducer<number, NumericArray>;
export function fillN(xs: Iterable<number>): NumericArray;
export function fillN(start: number, xs: Iterable<number>): NumericArray;
export function fillN(...args: any[]): any {
Expand Down
2 changes: 1 addition & 1 deletion packages/transducers/src/filter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export function filter<T>(
export function filter<T>(pred: Predicate<T>, src?: Iterable<T>): any {
return isIterable(src)
? iterator1(filter(pred), src)
: (rfn: Reducer<any, T>) => {
: (rfn: Reducer<T, any>) => {
const r = rfn[2];
return compR(rfn, (acc, x: T) => (pred(x) ? r(acc, x) : acc));
};
Expand Down
2 changes: 1 addition & 1 deletion packages/transducers/src/flatten-with.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ export function flattenWith<A>(
): any {
return isIterable(src)
? iterator(flattenWith(fn), isString(src) ? <any>[src] : src)
: (rfn: Reducer<any, A>) => {
: (rfn: Reducer<A, any>) => {
const reduce = rfn[2];
const flatten = (acc: any, x: any) => {
const xx = fn(x);
Expand Down
4 changes: 2 additions & 2 deletions packages/transducers/src/frequencies.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ import { count } from "./count.js";
import { groupByMap } from "./group-by-map.js";
import { $$reduce } from "./reduce.js";

export function frequencies<A>(): Reducer<Map<A, number>, A>;
export function frequencies<A>(): Reducer<A, Map<A, number>>;
export function frequencies<A>(xs: Iterable<A>): Map<A, number>;
export function frequencies<A, B>(key: Fn<A, B>): Reducer<Map<B, number>, A>;
export function frequencies<A, B>(key: Fn<A, B>): Reducer<A, Map<B, number>>;
export function frequencies<A, B>(
key: Fn<A, B>,
xs: Iterable<A>
Expand Down
12 changes: 6 additions & 6 deletions packages/transducers/src/group-binary.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ const branchPred =
* data structures. Any value can be indexed, as long as a numeric
* representation (key) can be obtained. This numeric key is produced by the
* supplied `key` function. IMPORTANT: the returned values MUST be unsigned and
* less than the provided bit length (i.e. `0 .. (2^bits) - 1` range).
* less than the provided bit length (i.e. in the `[0,2**bits)` interval).
*
* By default the tree is constructed using plain objects for branches, with
* left branches stored as "l" and right ones as "r". The original values are
Expand Down Expand Up @@ -48,8 +48,8 @@ const branchPred =
* import { groupBinary, identity, push, reduce } from "@thi.ng/transducers";
*
* tree = reduce(
* groupBinary(4, identity, ()=>[], push(), 0, 1),
* [1,2,3,4,5,6,7]
* groupBinary(4, identity, () => [], push(), 0, 1),
* [1, 2, 3, 4, 5, 6, 7]
* )
*
* tree[0][1][0][1] // 0101 == 5 in binary
Expand Down Expand Up @@ -90,12 +90,12 @@ export const groupBinary = <T>(
bits: number,
key: Fn<T, number>,
branch?: Fn0<IObjectOf<T[]>>,
leaf?: Reducer<any, T>,
leaf?: Reducer<T, any>,
left: PropertyKey = "l",
right: PropertyKey = "r"
): Reducer<any, T> => {
): Reducer<T, any> => {
const init = branch || (() => ({}));
let rfn: Reducer<any, T> = groupByObj({
let rfn: Reducer<T, any> = groupByObj({
key: branchPred(key, 1, left, right),
group: leaf || push(),
});
Expand Down
9 changes: 4 additions & 5 deletions packages/transducers/src/group-by-map.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,19 @@ import { $$reduce } from "./reduce.js";

export function groupByMap<SRC, KEY, GROUP>(
opts?: Partial<GroupByOpts<SRC, KEY, GROUP>>
): Reducer<Map<KEY, GROUP>, SRC>;
): Reducer<SRC, Map<KEY, GROUP>>;
export function groupByMap<SRC, GROUP>(xs: Iterable<SRC>): Map<SRC, GROUP>;
export function groupByMap<SRC, KEY, GROUP>(
opts: Partial<GroupByOpts<SRC, KEY, GROUP>>,
xs: Iterable<SRC>
): Map<KEY, GROUP>;
export function groupByMap<SRC, KEY, GROUP>(...args: any[]): any {
const res = $$reduce(groupByMap, args);
if (res !== undefined) {
return res;
}
if (res !== undefined) return res;

const opts = __groupByOpts<SRC, KEY, GROUP>(args[0]);
const [init, complete, reduce] = opts.group;
return <Reducer<Map<KEY, GROUP>, SRC>>[
return <Reducer<SRC, Map<KEY, GROUP>>>[
() => new Map(),
(acc) => {
for (let k of acc.keys()) {
Expand Down
9 changes: 4 additions & 5 deletions packages/transducers/src/group-by-obj.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,19 @@ import { $$reduce } from "./reduce.js";

export function groupByObj<SRC, GROUP>(
opts?: Partial<GroupByOpts<SRC, PropertyKey, GROUP>>
): Reducer<IObjectOf<GROUP>, SRC>;
): Reducer<SRC, IObjectOf<GROUP>>;
export function groupByObj<SRC>(xs: Iterable<SRC>): IObjectOf<SRC[]>;
export function groupByObj<SRC, GROUP>(
opts: Partial<GroupByOpts<SRC, PropertyKey, GROUP>>,
xs: Iterable<SRC>
): IObjectOf<GROUP>;
export function groupByObj<SRC, GROUP>(...args: any[]): any {
const res = $$reduce(groupByObj, args);
if (res) {
return res;
}
if (res) return res;

const opts = __groupByOpts<SRC, PropertyKey, GROUP>(args[0]);
const [_init, complete, _reduce] = opts.group;
return <Reducer<IObjectOf<GROUP>, SRC>>[
return <Reducer<SRC, IObjectOf<GROUP>>>[
() => ({}),
(acc) => {
for (let k in acc) {
Expand Down
2 changes: 1 addition & 1 deletion packages/transducers/src/interleave.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export function interleave<A, B>(
export function interleave<A, B>(sep: any, src?: Iterable<A>): any {
return isIterable(src)
? iterator(interleave(sep), src)
: (rfn: Reducer<any, A | B>) => {
: (rfn: Reducer<A | B, any>) => {
const r = rfn[2];
const _sep: Fn0<B> =
typeof sep === "function" ? sep : () => sep;
Expand Down
2 changes: 1 addition & 1 deletion packages/transducers/src/internal/drain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { isReduced } from "../reduced.js";
* @internal
*/
export const __drain =
<T>(buf: T[], complete: Fn<any, any>, reduce: ReductionFn<any, T>) =>
<T>(buf: T[], complete: Fn<any, any>, reduce: ReductionFn<T, any>) =>
(acc: T[]) => {
while (buf.length && !isReduced(acc)) {
acc = reduce(acc, buf.shift()!);
Expand Down
2 changes: 1 addition & 1 deletion packages/transducers/src/interpose.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export function interpose<A, B>(
export function interpose<A, B>(sep: any, src?: Iterable<A>): any {
return isIterable(src)
? iterator(interpose(sep), src)
: (rfn: Reducer<any, A | B>) => {
: (rfn: Reducer<A | B, any>) => {
const r = rfn[2];
const _sep: Fn0<B> =
typeof sep === "function" ? sep : () => sep;
Expand Down
Loading

0 comments on commit 95c43f3

Please sign in to comment.