A very simple coding problem is often encountered early on in one's journey through programming, as it is a precursor to more interesting problems. It usually reads something like this:
Given an alphanumeric input of type {string},
write a function that returns a string in the reverse order
We'll use this familiar problem as the contrived basis for demonstrating the awesome possibilities of functional pipes.
Now, some may well have gotten cozy with the use of slick libraries like ramda and lodash, but perhaps fewer have a solid understanding of the mechanisms used.
The star player
We'll start by taking a patient look at the custom JavaScript pipe implementation that we'll use in the examples to come.
I've also included a more traditional syntax for all the mustache heralds out there (let us know in the comments what syntax you prefer!):
const pipe =
(...functions) =>
(val) =>
functions.reduce((prev, func) => func(prev), val);
//
// OR
//
function pipeB(...functions) {
return function(val) {
return functions.reduce((prev, func) => {
return func(val)
}, val)
}
};
Those of us accustomed to writing Object-Oriented programs may find this syntax foreign. It uses functional techniques like currying and closures that will allow us to wrap, or decorate, our other functions.
The key operation, though, is the reduce that we call on our functions.
Let's break this down a bit.
We can deduce that:
- The pipe accepts a series of functions and returns a function.
- That function accepts a value and returns a reduce function that we call on our series of functions.
- Every time our reducer iterates, one of our functions consumes the value and passes its output to the next function
What does all that do for us, then?
So, every one of the functions that we supply to the pipe will consume our value in a step-by-step sequence, one after the other. Here's where the concept of a pipe and it's semantic exactness begins to wax into full shape.
Imagining the purpose and form of a series of interconnected pipes, we can lay out a few functions with the expectation that they will be similarly pieced together:
const splitString = (string) => string.split("");
const reverseArray = (splitString) => splitString.reverse();
const joinArray = (reversedArray) => reversedArray.join("");
Now let's create a custom flow with the pieces of our pipe:
const reverseStrFlow = pipe(splitString, reverseArray, joinArray);
The result here is a new function that accepts a value, so consuming our input will look like this:
const reversedString = reverseStrFlow(stringToReverse);
Finally, here is our code all together with example i/o:
const pipe =
(...functions) =>
(val) =>
functions.reduce((prev, func) => func(prev), val);
const splitString = (string) => string.split("");
const reverseArray = (splitString) => splitString.reverse();
const joinArray = (reversedArray) => reversedArray.join("");
const reverseStrFlow = pipe(splitString, reverseArray, joinArray);
const testString = "peloponnesian pumpkin";
const reversedString = reverseStrFlow(testString);
// expected output: "nikpmup naisennopolep"
For the record, one would rarely find meaningful need to write this much code for a problem slayable by a sexy one-liner:
const reverseString = (string) => string.split("").reverse().join("")
I only hope to diminish the what in order to clarify the how:
By flowing our value through a pipe made of functional pieces!
A key point in beginning to imagine the usefulness of this technique is to remember that we can create any number of custom pipes with any number of functional pieces that we need. This makes the composition of complex operations possible with less code.
The benefits of the compositional style are numerous:
Reusability
Building in pieces allows for many possible unique combinations
Testability
Test for simple inputs and outputs
Ease of debugging
Smaller pieces, smaller problems
Avoid the pitfalls of inheritance
Ditch complex inheritance hierarchies that carry dead weight
...and many more
Now that we've tantalized your tasters with some functional flavors, let's wrap things up with a final working example, this time with syntax highlighting:
Full Code on Repl
I'm honored if you've read this far ...
Keep going, keep growing, and don't forget to have fun!
Until next time,
Joshua
Top comments (0)
Some comments have been hidden by the post's author - find out more