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.
Top comments (2)
I particularly like the jsdocs comments.
I LOOOOOOOOOOOOOVE JSDoc