In mathematics, A function is A set-to-set mapping, that is, the elements in set A, after processing, are mapped to elements in set B. It can be expressed simply as follows:

f(A)->B

Functional programming, as the name suggests, is to program in a functional way. The origin of functional programming can be traced back to the lambda calculus in the early 20th century. In the λ calculus, functions take only one argument, and in order to implement multiple arguments, there were functions called Currying, which are essentially syntactic candy for single-argument functions.

The so-called Corrification can be understood as converting a multi-parameter function into a single-parameter function chain, which is simply expressed as follows:

f(X1, X2, … , Xn)->->g(X1)(X2)… (Xn)

Currification is not often used in daily front-end development, but it is often asked in interviews, and I suspect that the person who originally asked this question was testing the candidate’s understanding of functional programming.

Here’s a classic interview question to start with:

Implement the addition function add() so that it satisfies:

add(1.2.3); / / 6
add(1.2) (3); / / 6
add(1) (2) (3); / / 6
Copy the code

First, to implement chained calls, the return value of add is, of course, a function; Second, because the same addition is being called continuously here, it’s easy to think of recursion calling itself.

Let’s start with a simple version:

let sum = 0;
function add() {
    sum += Array.from(arguments).reduce((x, y) = > x + y);
    add.toString = () = > sum; // console.log is actually called function.tostring ()
    return add;
}
Copy the code

This is fine, but you need to manually reset sum to zero every time you call it, otherwise sum will be contaminated by the previous calculation.

It’s easy to imagine using closures to solve this problem, implementing a purely functional version of add() :

function add() {
    // The arguments are summed and saved to total the first time they are called
    let total = Array.from(arguments).reduce((x, y) = > x + y);
    // Use the closure feature to save total
    const _add = function () { // Note that arrow functions cannot be written here
        total += Array.from(arguments).reduce((x, y) = > x + y);
        return _add;
    }
    _add.toString = () = > total;
    return _add;
}
Copy the code

As an extra note, don’t go looking for something with a hammer in your hand:

Corrified functions can be used in any programming language that supports closures; However, non-Coriolization functions are usually preferred for efficiency reasons, since most function calls can avoid some of the overhead of application and closure creation.

— Wikipedia

The add() function is not elegant enough. Reduce is called twice. We can actually collect all the arguments and call it once:

function add() {
    // Save the parameters to an array
    const arr = Array.from(arguments);
    // Use the closure feature to save total
    const _add = function () { // Note that arrow functions cannot be written herearr.push(... Array.from(arguments));
        return _add;
    }
    _add.toString = () = > Array.from(arr).reduce((x, y) = > x + y);
    return _add;
}
Copy the code

Let’s take another layer of abstraction and convert an ordinary function into a function of a Corrified function:

function fnToCurry(fn) {
    const curry = function () {
        // The first time it is called, the arguments are stored in the array
        const arr = Array.from(arguments);
        // Use the closure feature to save total
        const _fn = function () { // Note that arrow functions cannot be written herearr.push(... Array.from(arguments));
            return _fn;
        }
        _fn.toString = () = >fn(... arr);return _fn;
    }
    return curry; // Finally return the currified function
}
Copy the code

Usage Examples:

function f1() {
    return Array.from(arguments).reduce((x, y) = > x + y);
}
// Kerrize f1
const f2 = fnToCurry(f1);
console.log(f2(1.2.3)); / / 6
console.log(f2(1) (2) (3)); / / 6
Copy the code

The above currified function will execute immediately when called. If we want the currified function not to execute immediately, for example, only if the parameter we pass contains “exec”, we can write:

function fnToDelayCurry(fn) {
    const curry = function () {
        const arr = Array.from(arguments)
        // Use the closure feature to save total
        const _fn = function () { // Note that arrow functions cannot be written here
            // Execute immediately when the input parameter has exec flag
            // Check whether the passed parameter is executed
            // The parameter containing exec will not be saved in the ARR. To save exec, move the push in else to the outer layer
            if (Array.from(arguments).indexOf("exec") > -1) {
                _fn.toString = () = >fn(... arr); }else {
                _fn.toString = Function.toString; arr.push(... Array.from(arguments));
            }
            return _fn;
        }
        return _fn;
    }
    return curry;
}
Copy the code

Usage Examples:

function delayFn() {
    const args = Array.from(arguments);
    return args.join("~");
}
console.log(fnToDelayCurry(delayFn)(1) (2) (3) (4.5)); // Print the function _fn
console.log(fnToDelayCurry(delayFn)(1) (2) (3) (4.5) ("exec")); / / 1 ~ 2 ~ 3 ~ 4 ~ 5
Copy the code

The above Demo code is here: jsrun.net/ciUKp/edit

References:

  1. Corey [wiki] : en.wikipedia.org/wiki/Curryi…
  2. Closure: developer.mozilla.org/zh-CN/docs/…
  3. The origin of the lambda calculus – functional language zhuanlan.zhihu.com/p/164700404

The articles

Modern package management tool: PNPM

Hardcore! First bullet: UpdateNotifier

What’s it like to go from front to back

When a programmer meets a product manager who can write code……

Hand write a Webpack Plugin

Hand-write a Webpack loader

This pot I carry……

ES2021 new features

Beat Magic with Magic: front-end code normalization

Hand to hand to teach you to build scaffolding

Build NPM private library by hand

requestAnimationFrame