packages/compose/src/comp.ts
import type { Fn, FnA, FnAny, FnAnyA } from "@thi.ng/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>;
export function comp<A, B, C, D>(
a: Fn<B, A>,
b: Fn<C, B>,
c: Fn<D, C>,
d: FnAny<D>
): FnAny<A>;
export function comp<A, B, C, D, E>(
a: Fn<B, A>,
b: Fn<C, B>,
c: Fn<D, C>,
d: Fn<E, D>,
e: FnAny<D>
): FnAny<A>;
export function comp<A, B, C, D, E, F>(
a: Fn<B, A>,
b: Fn<C, B>,
c: Fn<D, C>,
d: Fn<E, D>,
e: Fn<F, E>,
f: FnAny<F>
): FnAny<A>;
export function comp<A, B, C, D, E, F, G>(
a: Fn<B, A>,
b: Fn<C, B>,
c: Fn<D, C>,
d: Fn<E, D>,
e: Fn<F, E>,
f: Fn<G, F>,
g: FnAny<G>
): FnAny<A>;
export function comp<A, B, C, D, E, F, G, H>(
a: Fn<B, A>,
b: Fn<C, B>,
c: Fn<D, C>,
d: Fn<E, D>,
e: Fn<F, E>,
f: Fn<G, F>,
g: Fn<H, G>,
h: FnAny<H>
): FnAny<A>;
export function comp<A, B, C, D, E, F, G, H, I>(
a: Fn<B, A>,
b: Fn<C, B>,
c: Fn<D, C>,
d: Fn<E, D>,
e: Fn<F, E>,
f: Fn<G, F>,
g: Fn<H, G>,
h: Fn<I, H>,
i: FnAny<I>
): FnAny<A>;
export function comp<A, B, C, D, E, F, G, H, I, J>(
a: Fn<B, A>,
b: Fn<C, B>,
c: Fn<D, C>,
d: Fn<E, D>,
e: Fn<F, E>,
f: Fn<G, F>,
g: Fn<H, G>,
h: Fn<I, H>,
i: Fn<J, I>,
j: FnAny<J>
): FnAny<A>;
export function comp<A, B, C, D, E, F, G, H, I, J>(
a: Fn<B, A>,
b: Fn<C, B>,
c: Fn<D, C>,
d: Fn<E, D>,
e: Fn<F, E>,
f: Fn<G, F>,
g: Fn<H, G>,
h: Fn<I, H>,
i: Fn<J, I>,
j: Fn<any, J>,
...fns: FnAny<any>[]
): FnAny<A>;
export function comp(...fns: any[]): any {
let [a, b, c, d, e, f, g, h, i, j] = fns;
switch (fns.length) {
case 0:
illegalArity(0);
case 1:
return a;
case 2:
return (...xs: any[]) => a(b(...xs));
case 3:
return (...xs: any[]) => a(b(c(...xs)));
case 4:
return (...xs: any[]) => a(b(c(d(...xs))));
case 5:
return (...xs: any[]) => a(b(c(d(e(...xs)))));
case 6:
return (...xs: any[]) => a(b(c(d(e(f(...xs))))));
case 7:
return (...xs: any[]) => a(b(c(d(e(f(g(...xs)))))));
case 8:
return (...xs: any[]) => a(b(c(d(e(f(g(h(...xs))))))));
case 9:
return (...xs: any[]) => a(b(c(d(e(f(g(h(i(...xs)))))))));
case 10:
default:
const fn = (...xs: any[]) => a(b(c(d(e(f(g(h(i(j(...xs))))))))));
return fns.length === 10 ? fn : (<any>comp)(fn, ...fns.slice(10));
}
}
/**
* Similar to {@link comp}, but composes given functions in left-to-right order.
*/
export function compLeft<A>(a: FnAny<A>): FnAny<A>;
export function compLeft<A, B>(a: FnAny<A>, b: Fn<A, B>): FnAny<B>;
export function compLeft<A, B, C>(
a: FnAny<A>,
b: Fn<A, B>,
c: Fn<B, C>
): FnAny<C>;
export function compLeft<A, B, C, D>(
a: FnAny<A>,
b: Fn<A, B>,
c: Fn<B, C>,
d: Fn<C, D>
): FnAny<D>;
export function compLeft<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 compLeft<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 compLeft<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 compLeft<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 compLeft<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 compLeft<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 compLeft<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 compLeft(...fns: any[]): any {
return comp.apply(null, <any>fns.reverse());
}
/**
* @deprecated renamed to {@link compLeft}.
*/
export const compL = compLeft;
/**
* Async version of {@link comp}.
*/
export function compAsync<A, B>(a: FnAnyA<A>): FnAnyA<A>;
export function compAsync<A, B>(a: FnA<B, A>, b: FnAnyA<B>): FnAnyA<A>;
export function compAsync<A, B, C>(
a: FnA<B, A>,
b: FnA<C, B>,
c: FnAnyA<C>
): FnAnyA<A>;
export function compAsync<A, B, C, D>(
a: FnA<B, A>,
b: FnA<C, B>,
c: FnA<D, C>,
d: FnAnyA<D>
): FnAnyA<A>;
export function compAsync<A, B, C, D, E>(
a: FnA<B, A>,
b: FnA<C, B>,
c: FnA<D, C>,
d: FnA<E, D>,
e: FnAnyA<D>
): FnAnyA<A>;
export function compAsync<A, B, C, D, E, F>(
a: FnA<B, A>,
b: FnA<C, B>,
c: FnA<D, C>,
d: FnA<E, D>,
e: FnA<F, E>,
f: FnAnyA<F>
): FnAnyA<A>;
export function compAsync<A, B, C, D, E, F, G>(
a: FnA<B, A>,
b: FnA<C, B>,
c: FnA<D, C>,
d: FnA<E, D>,
e: FnA<F, E>,
f: FnA<G, F>,
g: FnAnyA<G>
): FnAnyA<A>;
export function compAsync<A, B, C, D, E, F, G, H>(
a: FnA<B, A>,
b: FnA<C, B>,
c: FnA<D, C>,
d: FnA<E, D>,
e: FnA<F, E>,
f: FnA<G, F>,
g: FnA<H, G>,
h: FnAnyA<H>
): FnAnyA<A>;
export function compAsync<A, B, C, D, E, F, G, H, I>(
a: FnA<B, A>,
b: FnA<C, B>,
c: FnA<D, C>,
d: FnA<E, D>,
e: FnA<F, E>,
f: FnA<G, F>,
g: FnA<H, G>,
h: FnA<I, H>,
i: FnAnyA<I>
): FnAnyA<A>;
export function compAsync<A, B, C, D, E, F, G, H, I, J>(
a: FnA<B, A>,
b: FnA<C, B>,
c: FnA<D, C>,
d: FnA<E, D>,
e: FnA<F, E>,
f: FnA<G, F>,
g: FnA<H, G>,
h: FnA<I, H>,
i: FnA<J, I>,
j: FnAnyA<J>
): FnAnyA<A>;
export function compAsync<A, B, C, D, E, F, G, H, I, J>(
a: FnA<B, A>,
b: FnA<C, B>,
c: FnA<D, C>,
d: FnA<E, D>,
e: FnA<F, E>,
f: FnA<G, F>,
g: FnA<H, G>,
h: FnA<I, H>,
i: FnA<J, I>,
j: FnA<any, J>,
...fns: FnAnyA<any>[]
): FnAnyA<A>;
export function compAsync(...fns: any[]): any {
return async (...xs: any[]) => {
let n = fns.length - 1;
let res = fns[n](...xs);
while (n-- > 0) {
res = await fns[n](res);
}
return res;
};
}