To undertake the last article “XDM, JS how functional programming? See this is enough! (a)”, we know a few basic concepts of functional programming.

Here is a brief review:

  1. The purpose of functional programming is to make the data flow more obvious and thus the code more readable.
  2. Functions require one or more inputs (ideally just one!) And an output, input output is explicit code will be better read;
  3. Closures are the basis of higher-order functions;
  4. Beware of anonymous functions;
  5. Discard this to point;

This article will focus on the second point in the function input, it is the basis of JS lightweight functional programming foundation, important important!!

Partial function

Transfer and the status quo

We often write code like this:

function ajax(url,data,callback) {
    // ..
}

function getPerson(data,cb) {
    ajax( "http://some.api/person", data, cb );
}

Copy the code

The Ajax function takes three incoming arguments and is called in the getPerson function, where the URL is determined and the data and CB arguments are waiting to be passed in. (Because most of the time the parameters are not currently determined, you need to wait for other functions to determine the operation before continuing to pass)

But our principle is: in the ideal case, only one input!

How can you optimize to achieve this?

We might be able to define the parameters further by enclosing another function, such as:

function getCurrentUser(cb) { ... // get CURRENT_USER_ID getPerson({user: CURRENT_USER_ID}, cb); }Copy the code

Thus, the data parameter is determined, and the cb parameter is still waiting to be passed in; The getCurrentUser function has only one incoming parameter!

The data transmission route is:

ajax(url,data,callback) => getPerson(data,cb) => getCurrentUser(cb)
Copy the code

In this way, the process of decreasing the number of function parameters is biased application.

GetCurrentUser (cb) is the partial function of getOrder(data,cb), which is the partial function of Ajax (URL,data,cb).

Imagine:

If a function looks like this:

function receiveMultiParam(a,b,c,...... ,x,y,z){ // .. }Copy the code

Do we need to manually specify the outer functions to be nested layer by layer as above?

Shows that we won’t do it!

Encapsulation partial

We just need to encapsulate a partial(..) Function:

function partial(fn,... presetArgs) { return function partiallyApplied(... laterArgs){ return fn( ... presetArgs, ... laterArgs ); }; }Copy the code

Its basic logic is:

var partial = (fn, ... presetArgs) => (... laterArgs) => fn( ... presetArgs, ... laterArgs );Copy the code

Take a function as an input! Remember what we said earlier:

A function that accepts or returns one or more functions is called a higher-order function.

We use partial() to implement the above example:

var getPerson = partial( ajax, "http://some.api/person" ); var getCurrentUser = partial( getPerson, { user: CURRENT_USER_ID } ); / / version 1Copy the code

Internal analysis of the following functions is important:

Operation mechanism

The internal workings of getPerson() are:

var getPerson = function partiallyApplied(... laterArgs) { return ajax( "http://some.api/person", ... laterArgs ); };Copy the code

The internal workings of getCurrentUser() are:

var getCurrentUser = function outerPartiallyApplied(... outerLaterArgs) { var getPerson = function innerPartiallyApplied(... innerLaterArgs){ return ajax( "http://some.api/person", ... innerLaterArgs ); }; return getPerson( { user: CURRENT_USER_ID }, ... outerLaterArgs ); }Copy the code

The data is passed:

getCurrentUser(outerLaterArgs) => getPerson(innerLaterArgs) => ajax(... params)Copy the code

With this extra layer of function wrapping, we’re able to deliver more powerful data,

We pass in partial() as the first argument the function we need to reduce the argument input, and the rest are presetArgs. We can write as many as we know. There are also indeterminate entry laterArgs that can be appended after they are determined.

Additional layers of higher-order function wrapping like this are the essence of functional programming!

“As we go through this series, we’re going to wrap a lot of functions around each other. Remember, this is functional programming!” — JavaScript Lightweight Functional Programming

In fact, we can implement getCurrentUser() as follows:

Var getCurrentUser = partial(ajax, "http://some.api/person", {user: CURRENT_USER_ID}); Var getCurrentUser = function partiallyApplied(... laterArgs) { return ajax( "http://some.api/person", { user: CURRENT_USER_ID }, ... laterArgs ); };Copy the code

Version 1, however, is a little clearer because it reuses functions that have already been defined. It’s supposed to be more in the spirit of functional programming!

Expand the partial

We can also use partial() :

function partial(fn,... presetArgs) { return function partiallyApplied(... laterArgs){ return fn( ... presetArgs, ... laterArgs ); }; }Copy the code

For example, add 3 to each item in the array [1,2,3,4,5].

Function add(x,y) {return x + y [1,2,3,4,5]. Map (function adder(val){return add(3, val); }); / /,5,6,7,8 [4]Copy the code

With the help of a partial () :

[1,2,3,4,5]. Map (partial(add, 3)); / /,5,6,7,8 [4]Copy the code

add(..) Cannot pass map(..) directly In the function, it can be passed in after being processed by partial application;

In fact, there are many variations of the partial() function:

Recall how we called Ajax functions earlier: Ajax (URL, data, cb). What should we do if we want to apply CB instead of specifying data and URL parameters later?

function reverseArgs(fn) { return function argsReversed(... args){ return fn( ... args.reverse() ); }; } function partialRight( fn, ... presetArgs ) { return reverseArgs( partial( reverseArgs( fn ), ... presetArgs.reverse() ) ); } var cacheResult = partialRight( ajax, function onResult(obj){ cache[obj.id] = obj; }); // After processing: cacheResult("http://some.api/person", {user: CURRENT_USER_ID});Copy the code

Currie,

Function currying is actually a special kind of partial function.

We use curry (..) Function to implement the previous Ajax (..) For example, it would look like this:

var curriedAjax = curry( ajax ); var personFetcher = curriedAjax( "http://some.api/person" ); var getCurrentUser = personFetcher( { user: CURRENT_USER_ID } ); getCurrentUser( function foundUser(user){ /* .. * /});Copy the code

Curlized function: a function that takes a single argument (number of arguments: 1) and returns another function that takes the next argument.

It converts a function from callable f(a, b, c) to callable f(a)(b)(c).

Implementation:

function curry(fn,arity = fn.length) { return (function nextCurried(prevArgs){ return function curried(nextArg){ var args = prevArgs.concat( [nextArg] ); if (args.length >= arity) { return fn( ... args ); } else { return nextCurried( args ); }}; }) ([]); }Copy the code

Stage summary

Why do we talk so much about partial(sum,1,2)(3) or currying (sum (1)(2)(3))?

First, it is obvious that partial functions, or currying, can separate the time and place of “specifying separate arguments”.

Second, and more importantly, when functions have only one parameter, we can relatively easily combine them. This unit function facilitates the subsequent combination function;

Wrapping a function into a higher-order function is the essence of functional programming!

Now, with the “partial function” weapon cannon, we will gradually blow the veil of JS lightweight functional programming ~

The above.

I am Anthony of nuggets, public account [Anthony of Nuggets], like 👍 follow 👀, output exposure input! Keep tracking!