Skip to content

Commit

Permalink
feat(transducers): add IXform interface & TxLike type alias, update r…
Browse files Browse the repository at this point in the history
…elated functions

- update all fns expecting Transducer args
- add internal ensureTransducer() helper
  • Loading branch information
postspectacular committed Jan 22, 2020
1 parent 3ede5af commit 49c62b7
Show file tree
Hide file tree
Showing 9 changed files with 191 additions and 133 deletions.
42 changes: 41 additions & 1 deletion packages/transducers/src/api.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { Comparator, Fn, IObjectOf } from "@thi.ng/api";

import { Reduced } from "./reduced";

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

export type TxLike<A, B> = Transducer<A, B> | IXform<A, B>;

export type ReductionFn<A, B> = (acc: A, x: B) => A | Reduced<A>;

export interface Reducer<A, B> extends Array<any> {
Expand All @@ -12,6 +13,45 @@ export interface Reducer<A, B> extends Array<any> {
[2]: ReductionFn<A, B>;
}

/**
* Interface for types able to provide some internal functionality (or
* derive some related transformation) as {@link Transducer}.
* Implementations of this interface can be directly passed to all
* functions in this package which are expecting a transducer arg.
*
* @example
* ```
* class Mul implements IXform<number, number> {
* constructor(public factor = 10) {}
*
* xform() { return map((x) => this.factor * x); }
* }
*
* transduce(new Mul(11), push(), range(4))
* // [0, 11, 22, 33, 44]
*
* // also usable w/ comp()
* transduce(
* comp(
* drop(1),
* new Mul(11),
* takeNth(2)
* ),
* push(),
* range(4)
* )
* // [11, 33]
* ```
*/
export interface IXform<A, B> {
/**
* Returns type specific operation as transducer. Internally called
* by functions in this package which expect transducer args. Users
* don't need to call this manually.
*/
xform(): Transducer<A, B>;
}

export interface IReducible<A, B> {
$reduce(rfn: ReductionFn<A, B>, acc: A): A | Reduced<A>;
}
Expand Down
138 changes: 69 additions & 69 deletions packages/transducers/src/func/comp.ts
Original file line number Diff line number Diff line change
@@ -1,101 +1,101 @@
import { comp as _comp } from "@thi.ng/compose";
import { Transducer } from "../api";
import { Transducer, TxLike } from "../api";
import { ensureTransducer } from "../internal/ensure";

/**
* Transducer composition. Returns new transducer which applies given
* transducers in left-to-right order.
*
* @remarks
* Fast (loop-free) paths are provided for up to 10 args (transducers).
*
* @param a -
*/
export function comp<A, B>(a: Transducer<A, B>): Transducer<A, B>;
export function comp<A, B>(a: TxLike<A, B>): Transducer<A, B>;
export function comp<A, B, C>(
a: Transducer<A, B>,
b: Transducer<B, C>
a: TxLike<A, B>,
b: TxLike<B, C>
): Transducer<A, C>;
export function comp<A, B, C, D>(
a: Transducer<A, B>,
b: Transducer<B, C>,
c: Transducer<C, D>
a: TxLike<A, B>,
b: TxLike<B, C>,
c: TxLike<C, D>
): Transducer<A, D>;
export function comp<A, B, C, D, E>(
a: Transducer<A, B>,
b: Transducer<B, C>,
c: Transducer<C, D>,
d: Transducer<D, E>
a: TxLike<A, B>,
b: TxLike<B, C>,
c: TxLike<C, D>,
d: TxLike<D, E>
): Transducer<A, E>;
export function comp<A, B, C, D, E, F>(
a: Transducer<A, B>,
b: Transducer<B, C>,
c: Transducer<C, D>,
d: Transducer<D, E>,
e: Transducer<E, F>
a: TxLike<A, B>,
b: TxLike<B, C>,
c: TxLike<C, D>,
d: TxLike<D, E>,
e: TxLike<E, F>
): Transducer<A, F>;
export function comp<A, B, C, D, E, F, G>(
a: Transducer<A, B>,
b: Transducer<B, C>,
c: Transducer<C, D>,
d: Transducer<D, E>,
e: Transducer<E, F>,
f: Transducer<F, G>
a: TxLike<A, B>,
b: TxLike<B, C>,
c: TxLike<C, D>,
d: TxLike<D, E>,
e: TxLike<E, F>,
f: TxLike<F, G>
): Transducer<A, G>;
export function comp<A, B, C, D, E, F, G, H>(
a: Transducer<A, B>,
b: Transducer<B, C>,
c: Transducer<C, D>,
d: Transducer<D, E>,
e: Transducer<E, F>,
f: Transducer<F, G>,
g: Transducer<G, H>
a: TxLike<A, B>,
b: TxLike<B, C>,
c: TxLike<C, D>,
d: TxLike<D, E>,
e: TxLike<E, F>,
f: TxLike<F, G>,
g: TxLike<G, H>
): Transducer<A, H>;
export function comp<A, B, C, D, E, F, G, H, I>(
a: Transducer<A, B>,
b: Transducer<B, C>,
c: Transducer<C, D>,
d: Transducer<D, E>,
e: Transducer<E, F>,
f: Transducer<F, G>,
g: Transducer<G, H>,
h: Transducer<H, I>
a: TxLike<A, B>,
b: TxLike<B, C>,
c: TxLike<C, D>,
d: TxLike<D, E>,
e: TxLike<E, F>,
f: TxLike<F, G>,
g: TxLike<G, H>,
h: TxLike<H, I>
): Transducer<A, I>;
export function comp<A, B, C, D, E, F, G, H, I, J>(
a: Transducer<A, B>,
b: Transducer<B, C>,
c: Transducer<C, D>,
d: Transducer<D, E>,
e: Transducer<E, F>,
f: Transducer<F, G>,
g: Transducer<G, H>,
h: Transducer<H, I>,
i: Transducer<I, J>
a: TxLike<A, B>,
b: TxLike<B, C>,
c: TxLike<C, D>,
d: TxLike<D, E>,
e: TxLike<E, F>,
f: TxLike<F, G>,
g: TxLike<G, H>,
h: TxLike<H, I>,
i: TxLike<I, J>
): Transducer<A, J>;
export function comp<A, B, C, D, E, F, G, H, I, J, K>(
a: Transducer<A, B>,
b: Transducer<B, C>,
c: Transducer<C, D>,
d: Transducer<D, E>,
e: Transducer<E, F>,
f: Transducer<F, G>,
g: Transducer<G, H>,
h: Transducer<H, I>,
i: Transducer<I, J>,
j: Transducer<J, K>
a: TxLike<A, B>,
b: TxLike<B, C>,
c: TxLike<C, D>,
d: TxLike<D, E>,
e: TxLike<E, F>,
f: TxLike<F, G>,
g: TxLike<G, H>,
h: TxLike<H, I>,
i: TxLike<I, J>,
j: TxLike<J, K>
): Transducer<A, K>;
export function comp<A, B, C, D, E, F, G, H, I, J, K>(
a: Transducer<A, B>,
b: Transducer<B, C>,
c: Transducer<C, D>,
d: Transducer<D, E>,
e: Transducer<E, F>,
f: Transducer<F, G>,
g: Transducer<G, H>,
h: Transducer<H, I>,
i: Transducer<I, J>,
j: Transducer<J, K>,
...fns: Transducer<any, any>[]
a: TxLike<A, B>,
b: TxLike<B, C>,
c: TxLike<C, D>,
d: TxLike<D, E>,
e: TxLike<E, F>,
f: TxLike<F, G>,
g: TxLike<G, H>,
h: TxLike<H, I>,
i: TxLike<I, J>,
j: TxLike<J, K>,
...fns: TxLike<any, any>[]
): Transducer<A, any>;
export function comp(...fns: any[]) {
fns = fns.map(ensureTransducer);
return _comp.apply(null, <any>fns);
}
7 changes: 7 additions & 0 deletions packages/transducers/src/internal/ensure.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { implementsFunction } from "@thi.ng/checks";
import { IXform, Transducer, TxLike } from "../api";

export const ensureTransducer = <A, B>(x: TxLike<A, B>) =>
implementsFunction(x, "xform")
? (<IXform<A, B>>x).xform()
: <Transducer<A, B>>x;
15 changes: 10 additions & 5 deletions packages/transducers/src/iterator.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { FnAny, NO_OP, SEMAPHORE } from "@thi.ng/api";
import { isIterable } from "@thi.ng/checks";
import { Reducer, Transducer } from "./api";
import { Reducer, Transducer, TxLike } from "./api";
import { ensureTransducer } from "./internal/ensure";
import { isReduced, unreduced } from "./reduced";
import { push } from "./rfn/push";

Expand All @@ -12,10 +13,10 @@ import { push } from "./rfn/push";
* @param xs -
*/
export function* iterator<A, B>(
xform: Transducer<A, B>,
xform: TxLike<A, B>,
xs: Iterable<A>
): IterableIterator<B> {
const rfn = <Reducer<B[], A>>xform(push());
const rfn = <Reducer<B[], A>>ensureTransducer(xform)(push());
const complete = rfn[1];
const reduce = rfn[2];
for (let x of xs) {
Expand All @@ -42,10 +43,12 @@ export function* iterator<A, B>(
* @param xs -
*/
export function* iterator1<A, B>(
xform: Transducer<A, B>,
xform: TxLike<A, B>,
xs: Iterable<A>
): IterableIterator<B> {
const reduce = (<Reducer<B, A>>xform([NO_OP, NO_OP, (_, x) => x]))[2];
const reduce = (<Reducer<B, A>>(
ensureTransducer(xform)([NO_OP, NO_OP, (_, x) => x])
))[2];
for (let x of xs) {
let y = reduce(<any>SEMAPHORE, x);
if (isReduced(y)) {
Expand All @@ -68,6 +71,8 @@ export function* iterator1<A, B>(
* @param xform -
* @param args -
* @param impl -
*
* @internal
*/
export const $iter = (
xform: FnAny<Transducer<any, any>>,
Expand Down
12 changes: 6 additions & 6 deletions packages/transducers/src/run.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Fn, NO_OP } from "@thi.ng/api";
import { IReducible, Reducer, Transducer } from "./api";
import { IReducible, Reducer, TxLike } from "./api";
import { transduce } from "./transduce";

const NO_OP_REDUCER: Reducer<void, any> = [NO_OP, NO_OP, NO_OP];
Expand All @@ -15,19 +15,19 @@ const NO_OP_REDUCER: Reducer<void, any> = [NO_OP, NO_OP, NO_OP];
* @param fx -
* @param xs -
*/
export function run<A>(tx: Transducer<A, any>, xs: Iterable<A>): void;
export function run<A>(tx: Transducer<A, any>, xs: IReducible<any, A>): void;
export function run<A>(tx: TxLike<A, any>, xs: Iterable<A>): void;
export function run<A>(tx: TxLike<A, any>, xs: IReducible<any, A>): void;
export function run<A, B>(
tx: Transducer<A, B>,
tx: TxLike<A, B>,
fx: Fn<B, void>,
xs: Iterable<A>
): void;
export function run<A, B>(
tx: Transducer<A, B>,
tx: TxLike<A, B>,
fx: Fn<B, void>,
xs: IReducible<any, A>
): void;
export function run<A, B>(tx: Transducer<A, B>, ...args: any[]) {
export function run<A, B>(tx: TxLike<A, B>, ...args: any[]) {
if (args.length === 1) {
transduce(tx, NO_OP_REDUCER, args[0]);
} else {
Expand Down
8 changes: 4 additions & 4 deletions packages/transducers/src/step.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Transducer } from "./api";
import { TxLike } from "./api";
import { ensureTransducer } from "./internal/ensure";
import { isReduced } from "./reduced";
import { push } from "./rfn/push";

Expand Down Expand Up @@ -38,9 +39,8 @@ import { push } from "./rfn/push";
*
* @param tx -
*/
export const step = <A, B>(tx: Transducer<A, B>): ((x: A) => B | B[]) => {
const [_, complete, reduce] = tx(push());
_;
export const step = <A, B>(tx: TxLike<A, B>): ((x: A) => B | B[]) => {
const { 1: complete, 2: reduce } = ensureTransducer(tx)(push());
let done = false;
return (x: A) => {
if (!done) {
Expand Down
20 changes: 13 additions & 7 deletions packages/transducers/src/transduce.ts
Original file line number Diff line number Diff line change
@@ -1,30 +1,36 @@
import { illegalArity } from "@thi.ng/errors";
import { IReducible, Reducer, Transducer } from "./api";
import {
IReducible,
Reducer,
Transducer,
TxLike
} from "./api";
import { ensureTransducer } from "./internal/ensure";
import { reduce } from "./reduce";
import { map } from "./xform/map";

export function transduce<A, B, C>(
tx: Transducer<A, B>,
tx: TxLike<A, B>,
rfn: Reducer<C, B>
): Transducer<Iterable<A>, C>;
export function transduce<A, B, C>(
tx: Transducer<A, B>,
tx: TxLike<A, B>,
rfn: Reducer<C, B>,
xs: Iterable<A>
): C;
export function transduce<A, B, C>(
tx: Transducer<A, B>,
tx: TxLike<A, B>,
rfn: Reducer<C, B>,
xs: IReducible<C, A>
): C;
export function transduce<A, B, C>(
tx: Transducer<A, B>,
tx: TxLike<A, B>,
rfn: Reducer<C, B>,
acc: C,
xs: Iterable<A>
): C;
export function transduce<A, B, C>(
tx: Transducer<A, B>,
tx: TxLike<A, B>,
rfn: Reducer<C, B>,
acc: C,
xs: IReducible<C, A>
Expand All @@ -44,5 +50,5 @@ export function transduce(...args: any[]): any {
default:
illegalArity(args.length);
}
return reduce(args[0](args[1]), acc, xs);
return reduce(ensureTransducer(args[0])(args[1]), acc, xs);
}

0 comments on commit 49c62b7

Please sign in to comment.