DEV Community

Functional Javascript
Functional Javascript

Posted on

A Functional Pipeline Example

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

pipeBase

/*
@func
a non-typed pipe
- that can dynamically handle sync or async funcs

@usage
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);
};
Enter fullscreen mode Exit fullscreen mode

pipeStr

/**
@func
a strongly-typed pipe that is invoked with a supplied str

@clientcode
const p1 = pipeStr(f1, f2, f3);
p1("");

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

1.

/**
@func
split by \n chars

@notes
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");

Enter fullscreen mode Exit fullscreen mode

2.

/**
@func
trim each str in arr of strs

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

@param {string[]} a
@return {string[]}
*/
export const mapTrim = a => a.map(s => isStr(s) ? s.trim() : s);
Enter fullscreen mode Exit fullscreen mode

3.

/**
@func
only remove empty str elems in an arr

@notes
applies trim() before comparing

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

4.

/**
@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);
Enter fullscreen mode Exit fullscreen mode

5.

/**
@func
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");
Enter fullscreen mode Exit fullscreen mode

6.

/*
@func
replace a substring with another substring in a haystack of text

@cons
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);

Enter fullscreen mode Exit fullscreen mode

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(
  splitByNewline,
  mapTrim,
  filterOutEmptyStrs,
  filterNotStartsWith("//"), //exclude comments
  joinByNewLine,
  replaceByRegex(/\n/g, " && "), lStr,
);
Enter fullscreen mode Exit fullscreen mode

an example feature usage

lBashExecChain(`
pwd
git config -l --local
git show-branch
git status
git stash list
git stash --include-untracked
git pull
git stash pop
lerna bootstrap
`);
Enter fullscreen mode Exit fullscreen mode

Final Notes

P.S.

If you have any questions, let me know.

Top comments (2)

Collapse
 
johnkazer profile image
John Kazer

I particularly like the jsdocs comments.

Collapse
 
functional_js profile image
Functional Javascript

I LOOOOOOOOOOOOOVE JSDoc

JSDoc usage example