This is the third article in the JS How to Program functionally series. Like 👍, follow 👀 and keep track of 😄

The first two Portals:

  • “XDM, JS how functional programming? See this is enough!”
  • “XDM, JS how to function programming? See this is enough!

In Part 2, we talked about the foundation of the foundation, the important important – “partial functions,” function encapsulation, to achieve the purpose of reducing the number of arguments passed, without the need to manually specify the argument.

More importantly:

When functions have only one parameter, we can relatively easily combine them. This unit function facilitates the subsequent combination function;

Yes, this is about combinatorial functions. It’s the most important thing in functional programming!

Combination function

meaning

Functional programming is like lego!

Lego has all kinds of parts and we put them together to make a larger component or model.

Functional programming also has functions of various functions that we assemble together to achieve a specific function.

Let’s look at an example where we use these two functions to parse a text string:

function words(str) { return String( str ) .toLowerCase() .split( /\s|\b/ ) .filter( function alpha(v){ return /^[\w]+$/.test( v ); }); } function unique(list) { var uniqList = []; for (let i = 0; i < list.length; i++) { if (uniqList.indexOf( list[i] ) === -1 ) { uniqList.push( list[i] ); } } return uniqList; } var text = "To compose two functions together"; var wordsFound = words( text ); var wordsUsed = unique( wordsFound ); wordsUsed; // ["to", "compose", "two", "functions", "together"]Copy the code

Don’t look too closely, just know that we first used words to handle text, then unique to handle wordsFound;

This process is analogous to the production of goods on the production line, the production line.

Imagine if you were a factory owner, how else would you optimize processes and save costs?

Here the author gives a solution: remove the conveyor belt!

To reduce the intermediate variable, we can call this:

var wordsUsed = unique( words( text ) );

wordsUsed
Copy the code

Indeed, less intermediate variables, more clarity, can you optimize again?

We can further encapsulate the entire process into a function:

function uniqueWords(str) {
    return unique( words( str ) );
}

uniqueWords(text)
Copy the code

So it’s like a black box, you don’t need to know what’s going on inside, you just need to know what’s going into the box! What is the output! Input and output clear, clear function, very “clean”! As shown in figure:

In the meantime, it can be moved around or reassembled.

Back inside the uniqueWords() function, its data flow is also clear:

uniqueWords <-- unique <-- words <-- text
Copy the code

Packaging box

The encapsulation of uniqueWords box above is very nice. If we want to constantly encapsulate the box like uniqueWords, do we need to write one by one?

function uniqueWords(str) { return unique( words( str ) ); } function uniqueWords_A(str) { return unique_A( words_A( str ) ); } function uniqueWords_B(str) { return unique_B( words_B( str ) ); }...Copy the code

So, in order to get lazy, we can write a more powerful function to automatically wrap the box:

function compose2(fn2,fn1) { return function composed(origValue){ return fn2( fn1( origValue ) ); }; } var compose2 = (fn2,fn1) => origValue => fn2(fn1(origValue));Copy the code

The call then looks like this:

var uniqueWords = compose2( unique, words );

var uniqueWords_A = compose2( unique_A, words_A );

var uniqueWords_B = compose2( unique_B, words_B );
Copy the code

Too clear!

Any combination

Up here, we combined two functions, and we could have actually combined N functions;

finalValue <-- func1 <-- func2 <-- ... <-- funcN <-- origValue
Copy the code

For example, use a compose function:

function compose(... FNS){return function composed(result){var list = fns.slice(); While (list.length > 0) {result = list.pop()(result); } return result; }; }, compose, compose, compose, compose, compose, compose fns) => result => { var list = fns.slice(); While (list.length > 0) {result = list.pop()(result); } return result; };Copy the code

Based on the previous uniqueWords(..) For example, let’s add one more function to handle (filter out strings of length 4 or less) :

function skipShortWords(list) {
    var filteredList = [];

    for (let i = 0; i < list.length; i++) {
        if (list[i].length > 4) {
            filteredList.push( list[i] );
        }
    }

    return filteredList;
}

var text = "To compose two functions together";

var biggerWords = compose( skipShortWords, unique, words );

var wordsUsed = biggerWords( text );

wordsUsed;
// ["compose", "functions", "together"]
Copy the code

So the compose function has three entries and they’re all functions. We can also use the properties of partial functions to achieve more:

function skipLongWords(list) { /* .. */ } var filterWords = partialRight( compose, unique, words ); Var biggerWords = filterWords(skipShortWords); var shorterWords = filterWords( skipLongWords ); biggerWords( text ); shorterWords( text );Copy the code

The filterWords function is a more featurely-specific variant (filtering strings based on the functionality of the first function).

Compose variant

compose(..) The function is important, but we probably won’t use the compose(..) we wrote ourselves in production. , and prefer to use the solution provided by a library. Understanding the underlying workings of functional programming is also useful in strengthening our understanding of functional programming.

For example: Compose (..) Another variant of this is implemented recursively:

function compose(... FNS) {var [fn1, fn2...rest] = fns.reverse(); var composedFn = function composed(... args){ return fn2( fn1( ... args ) ); }; if (rest.length == 0) return composedFn; return compose( ... rest.reverse(), composedFn ); }, compose, compose, compose, compose, compose, compose FNS) => {// Take the last two parameters var [fn1, fn2...rest] = fs.reverse (); // Take the last two parameters var [fn1, fn2...rest] = fs.reverse (); var composedFn = (... args) => fn2( fn1( ... args ) ); if (rest.length == 0) return composedFn; return compose( ... rest.reverse(), composedFn ); };Copy the code

Repeating actions through recursion is easier to understand than tracking the results in a loop, which may take more time to understand;

Based on the previous example, if we want to reverse the parameters:

var biggerWords = compose( skipShortWords, unique, words ); Var biggerWords = pipe(words, unique, skipShortWords);Copy the code

You just need to change compose(..) Internally implement this sentence:

. While (list.length > 0) {result = list.shift()(result); }...Copy the code

There is no essential difference between the two, although the order of parameters is reversed.

Abstract ability

Whether you wonder: under what circumstances can be encapsulated into the above “box”?

This is very test – abstract ability!

In fact, if two or more tasks have a common part, we can encapsulate it.

Such as:

function saveComment(txt) { if (txt ! = "") { comments[comments.length] = txt; } } function trackEvent(evt) { if (evt.name ! == undefined) { events[evt.name] = evt; }}Copy the code

Can be abstracted as:

function storeData(store,location,value) { store[location] = value; } function saveComment(txt) { if (txt ! = "") { storeData( comments, comments.length, txt ); } } function trackEvent(evt) { if (evt.name ! == undefined) { storeData( events, evt.name, evt ); }}Copy the code

When doing this kind of abstraction, there is a principle commonly referred to as DRY (Don’t Repeat Yourself), even though we spend time doing non-essential work.

Abstractions go a long way with your code! For example, the above example can be further upgraded:

function conditionallyStoreData(store,location,value,checkFn) { if (checkFn( value, store, location )) { store[location] = value; } } function notEmpty(val) { return val ! = ""; } function isUndefined(val) { return val === undefined; } function isPropUndefined(val,obj,prop) { return isUndefined( obj[prop] ); } function saveComment(txt) { conditionallyStoreData( comments, comments.length, txt, notEmpty ); } function trackEvent(evt) { conditionallyStoreData( events, evt.name, evt, isPropUndefined ); }Copy the code

So the if statement is also abstracted.

Abstraction is the process by which a programmer associates a name with a potentially complex piece of program so that the name can be thought of as representing the purpose of the function, rather than how it was implemented. By hiding irrelevant details, abstractions reduce conceptual complexity and allow programmers to focus on a maintainable subset of program content at any given time. — “Programming Language”

As we said at the beginning of this series, “It’s all about creating more readable and understandable code.”

On the other hand, abstraction is the process of turning imperative code into vocally imperative code. From “how” to “what.”

Imperative code is primarily concerned with describing what to do to accomplish a task exactly. Declarative code describes what the output should be and leaves the implementation to another part.

For example, ES6 added structural syntax:

Function getData() {return [1,2,3,4,5]; } // imperative var TMP = getData(); var a = tmp[0]; var b = tmp[3]; Var [a,, b] = getData();Copy the code

Developers need to be careful to use the right level of abstraction for each part of their program, not too much, not enough.

Stage summary

The combination of functions is intended to conform to the “declarative programming style,” which focuses on the “what” rather than the “what.”

It can route the output of one function call to another function call, and so on, with the help of compose(..) Or its variant implementation.

We expect the functions in the combination to be unitary (inputs and outputs should be one), which is also an important point mentioned in the previous section.

Compositing ———— declarative data streams ———— is one of the most important tools supporting other features of functional programming!

The above!

I am gold Anthony, the public number [gold Anthony], output exposure input, technology to see life.