raini-dev / pipes

Simple and reusable pipelines for function composition.

Home Page:https://raini.dev/docs/pipes

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

@raini/pipes

Build Status codecov npm npm licence: MIT code style: prettier versioning: @priestine/semantics

@raini/pipes is a set of composable blocks called Pipelines. Pipelines are lazy and do not get invoked until they are forked with process method.

Pipelines are Monoids and can be concatenated with other Pipelines using p.concat which allows joining separate sets of composed functions.

Features

  • Composition of functions via pipe
  • Helper pipe methods, e.g. pipeTap or extendPipe
  • Implements Semigroup (holds associativity) with p.concat
  • Implements Monoid (holds right identity and left identity) with p.concat and P.empty
  • PromisePipeline allows abstracting from using Promises in composed functions - promisePipeline.process returns the only Promise to work with
  • Can be used both in Node and in browsers (transpiled to ES5)

Installation

npm i -S @raini/pipes

Usage

PromisePipeline

import { PromisePipeline } from "@raini/pipes"
import * as rl from "readline"

const addSpaceIfMissing = (q: string): string => (q.endsWith(" ") ? q : q.concat(" "))
const toObject = (q: string) => ({ q })
const createReadLine = () => ({ rl: rl.createInterface(process.stdin, process.stdout) })
const askQuestionAsync = ({ rl, q }) => new Promise((res) => rl.question(q, (a: string) => res(a)))
const applyGreenColor = (x: string) => `\x1b[32m${x}\x1b[0m`
const log = console.log
const exit = () => process.exit(0)

PromisePipeline.of(addSpaceIfMissing)
  .pipe(toObject)
  .pipeExtend(createReadLine) // Extend argument object with return value
  .pipe(askQuestionAsync)
  .pipe(applyGreenColor)
  .pipeTap(log) // Execute function on the argument and return the argument
  .process(() => "What is the answer to life, the universe and everything?")
  .then(exit)

PromisePipeline Helpers

  • pipeP is equivalent to PromisePipeline.empty().pipe
  • pipeExtendP is equivalent to PromisePipeline.empty().pipeExtend
  • pipeTapP is equivalent to PromisePipeline.empty().pipeTap

SyncPipeline

import { SyncPipeline } from "@raini/pipes"

const isOdd = (num: number) => num % 2 == 0
const negate = <T>(f: (x: T) => any) => (x: T) => !f(x)
const filterOutOddNumbers = (nums: number[]) => nums.filter(negate(isOdd))
const multiplyBy2 = (num: number) => num * 2
const multiplyItemsBy2 = (nums: number[]) => nums.map(multiplyBy2)
const log = console.log

const result = SyncPipeline.of(filterOutOddNumbers)
  .pipeTap(log) // [ 1, 3, 5 ]
  .pipe(multiplyItemsBy2)
  .process(() => [1, 2, 3, 4, 5])

log(result) // [ 2, 6, 10 ]

// A fun thing using pipeExtend (instead of pipe) for multiplying items by 2

const result2 = SyncPipeline.of(filterOutOddNumbers)
  .pipeTap(log) // [ 1, 3, 5 ]
  .pipeExtend(multiplyItemsBy2)
  .process(() => [1, 2, 3, 4, 5])

log(result2) // [ 1, 3, 5, 2, 6, 10 ]

SyncPipeline Helpers

  • pipe is equivalent to SyncPipeline.empty().pipe
  • pipeExtend is equivalent to SyncPipeline.empty().pipeExtend
  • pipeTap is equivalent to SyncPipeline.empty().pipeTap

About

Simple and reusable pipelines for function composition.

https://raini.dev/docs/pipes

License:MIT License


Languages

Language:TypeScript 96.1%Language:Shell 2.0%Language:JavaScript 1.9%