Skip to content

Pipe ​

Performs left-to-right function composition. Each function takes the output of the previous one and ouput the result at last.

Every function's arguments must be unary except the first flow argument.

Example ​

Without pipe

ts
const plusOne = (n: number) => n + 1
const mulTwo = (n: number) => n * 2
const subThree = (n: number) => n - 3

subThree(mulTwo(plusOne(1))) // (1 + 1) * 2 - 3
subThree(mulTwo(plusOne(2))) // (2 + 1) * 2 - 3

With pipe and flow

ts
import { pipe } from 'sa-lambda/pipe'

pipe(
  1,
  plusOne,
  mulTwo,
  subThree
)

const f = flow(
  plusOne,
  mulTwo,
  subThree
)

f(1)
f(2)

API ​

pipe ​

ts
function pipe<A, B>(a: A, ab: PipeFn<A, B>): B
function pipe<A, B, C>(a: A, ab: PipeFn<A, B>, bc: PipeFn<B, C>): C
function pipe<A, B, C, D>(a: A, ab: PipeFn<A, B>, bc: PipeFn<B, C>, cd: PipeFn<C, D>): D
// and so on...
function pipe<A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R>(
  a: A,
  ab: PipeFn<A, B>,
  bc: PipeFn<B, C>,
  cd: PipeFn<C, D>,
  de: PipeFn<D, E>,
  ef: PipeFn<E, F>,
  fg: PipeFn<F, G>,
  gh: PipeFn<G, H>,
  hi: PipeFn<H, I>,
  ij: PipeFn<I, J>,
  jk: PipeFn<J, K>,
  kl: PipeFn<K, L>,
  lm: PipeFn<L, M>,
  mn: PipeFn<M, N>,
  no: PipeFn<N, O>,
  op: PipeFn<O, P>,
  pq: PipeFn<P, Q>,
  qr: PipeFn<Q, R>
): R
function pipe<A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, Other extends PipeFn<any, any>[]>(
  a: A,
  ab: PipeFn<A, B>,
  bc: PipeFn<B, C>,
  cd: PipeFn<C, D>,
  de: PipeFn<D, E>,
  ef: PipeFn<E, F>,
  fg: PipeFn<F, G>,
  gh: PipeFn<G, H>,
  hi: PipeFn<H, I>,
  ij: PipeFn<I, J>,
  jk: PipeFn<J, K>,
  kl: PipeFn<K, L>,
  lm: PipeFn<L, M>,
  mn: PipeFn<M, N>,
  no: PipeFn<N, O>,
  op: PipeFn<O, P>,
  pq: PipeFn<P, Q>,
  qr: PipeFn<Q, R>,
  ...fns: Other
): ReturnType<TupleLast<Other>>

Takes a value as the first function arguement, then chain the subsequent functions with each previous output in left-to-right sequence.

ts
pipe(0, n => n + 1, n => n + 2) ➔ 0 + 1 + 2

flow ​

ts
function flow<Args extends unknown[], A>(a: (...args: Args) => A): (...args: Args) => A
function flow<Args extends unknown[], A, B>(a: (...args: Args) => A, ab: PipeFn<A, B>): (...args: Args) => B
function flow<Args extends unknown[], A, B, C>(a: (...args: Args) => A, ab: PipeFn<A, B>, bc: PipeFn<B, C>): (...args: Args) => C
// and so on...
function flow<Args extends unknown[], A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R>(
  a: (...args: Args) => A,
  ab: PipeFn<A, B>,
  bc: PipeFn<B, C>,
  cd: PipeFn<C, D>,
  de: PipeFn<D, E>,
  ef: PipeFn<E, F>,
  fg: PipeFn<F, G>,
  gh: PipeFn<G, H>,
  hi: PipeFn<H, I>,
  ij: PipeFn<I, J>,
  jk: PipeFn<J, K>,
  kl: PipeFn<K, L>,
  lm: PipeFn<L, M>,
  mn: PipeFn<M, N>,
  no: PipeFn<N, O>,
  op: PipeFn<O, P>,
  pq: PipeFn<P, Q>,
  qr: PipeFn<Q, R>
): (...args: Args) => R
function flow<Args extends unknown[], A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, Other extends PipeFn<any, any>[]>(
  a: (...args: Args) => A,
  ab: PipeFn<A, B>,
  bc: PipeFn<B, C>,
  cd: PipeFn<C, D>,
  de: PipeFn<D, E>,
  ef: PipeFn<E, F>,
  fg: PipeFn<F, G>,
  gh: PipeFn<G, H>,
  hi: PipeFn<H, I>,
  ij: PipeFn<I, J>,
  jk: PipeFn<J, K>,
  kl: PipeFn<K, L>,
  lm: PipeFn<L, M>,
  mn: PipeFn<M, N>,
  no: PipeFn<N, O>,
  op: PipeFn<O, P>,
  pq: PipeFn<P, Q>,
  qr: PipeFn<Q, R>,
  ...fns: Other
): (...args: Args) => ReturnType<TupleLast<Other>>

The flow operator is similar as pipe, but the first function in flow is allowed to have arguements more than one.

The most different thing is the flow creates a curry pipe function so it has the ability to accpect the input arguments later. So according to this, it is often used to create a pipe that may be called many times with different input arguments.

ts
const f = flow(
  (a: number, b: number) => a + b,
  (n: number) => n + 1,
  (n: number) => n + 2
)

f(1, 2)
f(2, 3)

Released under the MIT License.