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.