A Functional Pipeline Example
Functional Javascript
Posted on September 2, 2020
Method Chaining (MC) vs Functional Pipelining (FP)
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
toexcludeComments
. Also fromreplaceByRegex
toreplaceNewlineWithDoubleAmpersand
. 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);
};
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);
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");
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);
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));
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);
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");
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);
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,
);
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
`);
Final Notes
This is not a fully working feature example because all the code is not present in this post. The focus of this post is to show a functional approach to software design.
There are some naming conventions that are very intuitive. I've listed common abbreviations used in JavaScript here:
https://dev.to/functional_js/popular-abbreviations-in-software-development-4ikk
P.S.
If you have any questions, let me know.
💖 💪 🙅 🚩
Functional Javascript
Posted on September 2, 2020
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.