Skip to content

Commit

Permalink
feat(compose): add threadFirst/Last, rename compI => compL
Browse files Browse the repository at this point in the history
  • Loading branch information
postspectacular committed Dec 26, 2018
1 parent ec6084b commit 0061b21
Show file tree
Hide file tree
Showing 6 changed files with 110 additions and 15 deletions.
4 changes: 3 additions & 1 deletion packages/compose/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,11 @@ This project is part of the
Functional composition helpers:

- [comp()](https://github.com/thi-ng/umbrella/tree/master/packages/compose/src/comp.ts)
- [compI()](https://github.com/thi-ng/umbrella/tree/master/packages/compose/src/comp.ts)
- [compL()](https://github.com/thi-ng/umbrella/tree/master/packages/compose/src/comp.ts)
- [juxt()](https://github.com/thi-ng/umbrella/tree/master/packages/compose/src/juxt.ts)
- [partial()](https://github.com/thi-ng/umbrella/tree/master/packages/compose/src/partial.ts)
- [threadFirst()](https://github.com/thi-ng/umbrella/tree/master/packages/compose/src/thread-first.ts)
- [threadLast()](https://github.com/thi-ng/umbrella/tree/master/packages/compose/src/thread-last.ts)

## Installation

Expand Down
41 changes: 28 additions & 13 deletions packages/compose/src/comp.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
import { Fn, FnAny } from "@thi.ng/api/api";
import { illegalArity } from "@thi.ng/errors/illegal-arity";

/**
* Returns the right-to-left composition of given functions. I.e. when
* the composed function is called, the given right most function is
* called first (supports any number of args) and its return value then
* used as sole argument for the next function etc. Eventually returns
* result of left-most function.
*/
export function comp<A, B>(a: FnAny<A>): FnAny<A>;
export function comp<A, B>(a: Fn<B, A>, b: FnAny<B>): FnAny<A>;
export function comp<A, B, C>(a: Fn<B, A>, b: Fn<C, B>, c: FnAny<C>): FnAny<A>;
Expand Down Expand Up @@ -38,21 +45,29 @@ export function comp(...fns: any[]): any {
case 10:
default:
const fn = (...xs: any[]) => a(b(c(d(e(f(g(h(i(j(...xs))))))))));
return fns.length === 10 ? fn : (<any>compI)(fn, ...fns.slice(10));
return fns.length === 10 ? fn : (<any>compL)(fn, ...fns.slice(10));
}
}

export function compI<A>(a: FnAny<A>): FnAny<A>;
export function compI<A, B>(a: FnAny<A>, b: Fn<A, B>): FnAny<B>;
export function compI<A, B, C>(a: FnAny<A>, b: Fn<A, B>, c: Fn<B, C>): FnAny<C>;
export function compI<A, B, C, D>(a: FnAny<A>, b: Fn<A, B>, c: Fn<B, C>, d: Fn<C, D>): FnAny<D>;
export function compI<A, B, C, D, E>(a: FnAny<A>, b: Fn<A, B>, c: Fn<B, C>, d: Fn<C, D>, e: Fn<D, E>): FnAny<E>;
export function compI<A, B, C, D, E, F>(a: FnAny<A>, b: Fn<A, B>, c: Fn<B, C>, d: Fn<C, D>, e: Fn<D, E>, f: Fn<E, F>): FnAny<F>;
export function compI<A, B, C, D, E, F, G>(a: FnAny<A>, b: Fn<A, B>, c: Fn<B, C>, d: Fn<C, D>, e: Fn<D, E>, f: Fn<E, F>, g: Fn<F, G>): FnAny<G>;
export function compI<A, B, C, D, E, F, G, H>(a: FnAny<A>, b: Fn<A, B>, c: Fn<B, C>, d: Fn<C, D>, e: Fn<D, E>, f: Fn<E, F>, g: Fn<F, G>, h: Fn<G, H>): FnAny<H>;
export function compI<A, B, C, D, E, F, G, H, I>(a: FnAny<A>, b: Fn<A, B>, c: Fn<B, C>, d: Fn<C, D>, e: Fn<D, E>, f: Fn<E, F>, g: Fn<F, G>, h: Fn<G, H>, i: Fn<H, I>): FnAny<I>;
export function compI<A, B, C, D, E, F, G, H, I, J>(a: FnAny<A>, b: Fn<A, B>, c: Fn<B, C>, d: Fn<C, D>, e: Fn<D, E>, f: Fn<E, F>, g: Fn<F, G>, h: Fn<G, H>, i: Fn<H, I>, j: Fn<I, J>): FnAny<J>;
export function compI<A, B, C, D, E, F, G, H, I, J>(a: FnAny<A>, b: Fn<A, B>, c: Fn<B, C>, d: Fn<C, D>, e: Fn<D, E>, f: Fn<E, F>, g: Fn<F, G>, h: Fn<G, H>, i: Fn<H, I>, j: Fn<I, J>, ...xs: Fn<any, any>[]): FnAny<any>;
export function compI(...fns: any[]): any {
/**
* Similar to `comp()`, but composes given functions in left-to-right order.
*/
export function compL<A>(a: FnAny<A>): FnAny<A>;
export function compL<A, B>(a: FnAny<A>, b: Fn<A, B>): FnAny<B>;
export function compL<A, B, C>(a: FnAny<A>, b: Fn<A, B>, c: Fn<B, C>): FnAny<C>;
export function compL<A, B, C, D>(a: FnAny<A>, b: Fn<A, B>, c: Fn<B, C>, d: Fn<C, D>): FnAny<D>;
export function compL<A, B, C, D, E>(a: FnAny<A>, b: Fn<A, B>, c: Fn<B, C>, d: Fn<C, D>, e: Fn<D, E>): FnAny<E>;
export function compL<A, B, C, D, E, F>(a: FnAny<A>, b: Fn<A, B>, c: Fn<B, C>, d: Fn<C, D>, e: Fn<D, E>, f: Fn<E, F>): FnAny<F>;
export function compL<A, B, C, D, E, F, G>(a: FnAny<A>, b: Fn<A, B>, c: Fn<B, C>, d: Fn<C, D>, e: Fn<D, E>, f: Fn<E, F>, g: Fn<F, G>): FnAny<G>;
export function compL<A, B, C, D, E, F, G, H>(a: FnAny<A>, b: Fn<A, B>, c: Fn<B, C>, d: Fn<C, D>, e: Fn<D, E>, f: Fn<E, F>, g: Fn<F, G>, h: Fn<G, H>): FnAny<H>;
export function compL<A, B, C, D, E, F, G, H, I>(a: FnAny<A>, b: Fn<A, B>, c: Fn<B, C>, d: Fn<C, D>, e: Fn<D, E>, f: Fn<E, F>, g: Fn<F, G>, h: Fn<G, H>, i: Fn<H, I>): FnAny<I>;
export function compL<A, B, C, D, E, F, G, H, I, J>(a: FnAny<A>, b: Fn<A, B>, c: Fn<B, C>, d: Fn<C, D>, e: Fn<D, E>, f: Fn<E, F>, g: Fn<F, G>, h: Fn<G, H>, i: Fn<H, I>, j: Fn<I, J>): FnAny<J>;
export function compL<A, B, C, D, E, F, G, H, I, J>(a: FnAny<A>, b: Fn<A, B>, c: Fn<B, C>, d: Fn<C, D>, e: Fn<D, E>, f: Fn<E, F>, g: Fn<F, G>, h: Fn<G, H>, i: Fn<H, I>, j: Fn<I, J>, ...xs: Fn<any, any>[]): FnAny<any>;
export function compL(...fns: any[]): any {
return comp.apply(null, fns.reverse());
}

/**
* @deprecated renamed to `compL`
*/
export const compI = compL;
2 changes: 2 additions & 0 deletions packages/compose/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
export * from "./comp";
export * from "./juxt";
export * from "./partial";
export * from "./thread-first";
export * from "./thread-last";
2 changes: 1 addition & 1 deletion packages/compose/src/juxt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export function juxt(...fns: Fn<any, any>[]) {
default:
return (x: any) => {
let res = new Array(fns.length);
for (let i = fns.length - 1; i >= 0; i--) {
for (let i = fns.length; --i >= 0;) {
res[i] = fns[i](x);
}
return res;
Expand Down
38 changes: 38 additions & 0 deletions packages/compose/src/thread-first.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { FnAny } from "@thi.ng/api/api";

/**
* Takes an `init` value and a number of functions and/or function
* tuples, consisting of: `[fn, ...args]`. Executes each function
* (or tuple) with the return value of the previous expression inserted
* as first argument, using `init` for the first expression.
*
* ```
* const neg = (x) => -x;
* const sub = (a, b) => a - b;
* const div = (a, b) => a / b;
*
* threadFirst(
* 5,
* neg, // -5
* [sub, 20], // -5 - 20 = -25
* [div, 10] // -25 / 10 = -2.5
* );
*
* // -2.5
* ```
*
* @see threadLast
*
* @param init
* @param fns
*/
export function threadFirst(init: any, ...fns: (FnAny<any> | [FnAny<any>, ...any[]])[]) {
return fns.reduce(
(acc, expr) =>
typeof expr === "function" ?
expr(acc) :
expr[0](acc, ...expr.slice(1))
,
init
);
}
38 changes: 38 additions & 0 deletions packages/compose/src/thread-last.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { FnAny } from "@thi.ng/api/api";

/**
* Takes an `init` value and a number of functions and/or function
* tuples, consisting of: `[fn, ...args]`. Executes each function
* (or tuple) with the return value of the previous expression inserted
* as last argument, using `init` for the first expression.
*
* ```
* const neg = (x) => -x;
* const sub = (a, b) => a - b;
* const div = (a, b) => a / b;
*
* threadLast(
* 5,
* neg, // -5
* [sub, 10], // 20 - (-5) = 15
* [div, 10] // 10 / 25 = 0.4
* );
*
* // 0.4
* ```
*
* @see threadFirst
*
* @param init
* @param fns
*/
export function threadLast(init: any, ...fns: (FnAny<any> | [FnAny<any>, ...any[]])[]) {
return fns.reduce(
(acc, expr) =>
typeof expr === "function" ?
expr(acc) :
expr[0](...expr.slice(1), acc)
,
init
);
}

0 comments on commit 0061b21

Please sign in to comment.