DEV Community


A Functional Pipeline Example

functional_js profile image Functional Javascript ・3 min read

Method Chaining (MC) vs Functional Pipelining (FP)

functional pipeline example

Notes on MC vs FP

Notice that the two approaches are very similar. If you can grok the MC, then you can also grok the FP approach

MC cons:

  • can only use the built-in methods.
  • cannot control the abstraction level.
  • very object-oriented (intermingles data and behavior).
  • weakly-typed.

FP pros:

  • full control to use any custom function.
  • you control the abstraction level. Notice in this example the FP funcs are are still domain agnostic but perform higher level, commonly reused behaviors. Notice 4 of the FP funcs are abstracted perfectly, because they take no args. There are 2 funcs that take args so I could abstract those from filterNotStartsWith to excludeComments. Also from replaceByRegex to replaceNewlineWithDoubleAmpersand. I haven't done this because they are not very popular behaviors, but the FP pipeline would read even more fluent.
  • separates data and behavior by using free (non-class bound) static funcs.
  • runtime-enforced strongly-typed.

Source Code Examples


a non-typed pipe
- that can dynamically handle sync or async funcs

this is a util func of the typed pipe funcs

@param {...Function} fns - one or more funcs spread to an arr of funcs
@return {(v: *) => *} - the first func in the pipe takes in any data, and the last func returns any data
export const pipeBase = (...fns) => v => {
  return fns.reduce((r, fn) => { // r = result or output of fn call
    if (isPromise(r)) {
      return r.then(fn);
    return fn(r);
  }, v);


a strongly-typed pipe that is invoked with a supplied str

const p1 = pipeStr(f1, f2, f3);

@param {...Function} fns
@return {(s: string) => *}
export const pipeStr = (...fns) => s => throwIfNotStr(s) || pipeBase(...fns)(s);


split by \n chars

use forceSingleNewline() beforehand if str contains multiple blank lines in a row

@param {string} s
@return {string[]}
export const splitByNewline = s => s.trim().split("\n");


trim each str in arr of strs

if the elem is a str, trim it
- otherwise leave it as is

@param {string[]} a
@return {string[]}
export const mapTrim = a => => isStr(s) ? s.trim() : s);


only remove empty str elems in an arr

applies trim() before comparing

@param {string[]} a
@return {string[]} arr with empty elems removed
export const filterOutEmptyStrs = a => a.filter(s => isNotEmptyStr(s));


@func complement
from the supplied arr, remove the elems that start with the supplied str chunk

@param {string} chunk
@return {(a: string[]) => string[]}
export const filterNotStartsWith = chunk => a => fil(s => !s.startsWith(chunk), a);


make a single str where each elem is placed on a new line

@param {string[]} a
@return {string} concatentated
export const joinByNewLine = a => a.join("\n");


replace a substring with another substring in a haystack of text

use the g flag to remove all matches
- otherwise it will just replace the first and return
case sensitive

@param {RegExp} n needleRegex
@param {string} r replacement
@return {(h: string) => string} // haystack -> a copy of the haystack with the needles replaced with the new values
export const replaceByRegex = (n, r) => h => h.replace(n, r);

final FP pipeline usage

@func util
supply a template string of bash commands
- and return the logged output

@param {string} a line-separated chain of bash commands
@return {string} chain of commands separated by &&
export const chainCmds = pipeStr(
  filterNotStartsWith("//"), //exclude comments
  replaceByRegex(/\n/g, " && "), lStr,

an example feature usage

git config -l --local
git show-branch
git status
git stash list
git stash --include-untracked
git pull
git stash pop
lerna bootstrap

Final Notes


If you have any questions, let me know.

Discussion (2)

Editor guide
johnkazer profile image
John Kazer

I particularly like the jsdocs comments.

functional_js profile image
Functional Javascript Author


JSDoc usage example