The translation

Functional programming is a programming style that attempts to pass a function as a parameter (that is, as a callback function) and return a function without the function side effect (that is, changing the state of the program). There are many languages that use this style of programming, including some popular ones such as JavaScript, Haskell, Clojure, Erlang, and Scala. Functional programming, with its ability to pass and return functions, introduces a number of concepts:

  • Pure functions
  • Currie,
  • One of the concepts of higher order functions that we’re going to look at is currie. In this article, we’ll look at how Colitisation works and how it plays a role in our work as software developers.

What is currying

Currying is a procedure in functional programming that takes a function with multiple arguments and converts it into a nested function queue, then returns a new function with the next expected inline argument. It keeps returning a new function (expecting the current argument, as we said before) until all the arguments are used up. These arguments are kept “alive” from being destroyed (taking advantage of closures) and are all used for execution when the last function in the currying chain returns and executes.

Curlization is the process of turning a function that has more than one arity into one that has fewer. — kbrainwave Note: The term arity refers to the number of arguments to a function. For example:

function fn(a, b) {
    //...
}
function _fn(a, b, c) {
    //...
}
Copy the code

Function fn takes two arguments (i.e. 2-arity) and _fn takes three arguments (i.e. 3-arity). Thus, curitisation transforms a function with multiple arguments into a series of functions with only one argument. Let’s look at a simple example:

function multiply(a, b, c) {
    return a * b * c;
}
Copy the code

This function takes three numbers, multiplies them, and returns the result.

Multiply (1, 2, 3); / / 6Copy the code

Let’s see how we can call the multiplication function with full arguments. Let’s create a curlized version of the function, and then see how to call the same function (and get the same results) in a series of calls.

function multiply(a) {
    return (b) => {
        return (c) => {
            return a * b * c
        }
    }
}
log(multiply(1)(2)(3)) // 6
Copy the code

We have converted the multiply(1,2,3) function call form to multiply(1)(2)(3) function call form. A single function has been transformed into a series of functions. To get a multiplication of three numbers 1, 2, and 3, the numbers are passed one after the other, each prepopulated for the next function inline call. We can separate the multiply(1)(2)(3) function call step for better understanding.

const mul1 = multiply(1);
const mul2 = mul1(2);
const result = mul2(3);
log(result); / / 6Copy the code

Let’s pass the arguments one by one. First pass the parameter 1 to multiply:

let mul1 = multiply(1);
Copy the code

The above code execution returns a function:

return (b) => {
        return (c) => {
            return a * b * c
        }
    }
Copy the code

For now, the variable mul1 will keep the above function definition, and this function will take a parameter b. We call function mul1, passing in argument 2:

let mul2 = mul1(2);
Copy the code

Function mul1 returns a third function

return (c) => {
            return a * b * c
        }
Copy the code

The returned function is now stored in the variable mul2. In essence, the variable mul2 can be interpreted this way:

mul2 = (c) => {
            return a * b * c
        }
Copy the code

When the function mul2 is called with argument 3,

const result = mul2(3);
Copy the code

The calculation is performed using the arguments passed in earlier: a=1, b=2, and the result is 6.

log(result); / / 6Copy the code

As a nested function, the mul2 function has access to the variable scope of external functions, namely the multiply and mul1 functions. This is why the mul2 function can multiply using variables defined in the completed function. Although the function has already returned and garbage collection has already been performed in memory. But its variables are somehow kept alive.

You can see that three numbers are passed one argument at a time, and each time a new function is returned, until all the arguments are used up. Here’s another example:

function volume(l,w,h) {
    returnl * w * h; } const aCylinder = volume(100,20,90) // 18000Copy the code

Above is a function that calculates the volume of any solid shape. This currie version will take an argument and return a function that will also take an argument and return a new function. The loop continues until the last argument is reached and the last function is returned. Then the multiplication of the previous argument and the last argument is performed.

function volume(l) {
    return (w) => {
        return (h) => {
            return l * w * h
        }
    }
}
const aCylinder = volume(100)(20)(90) // 180000
Copy the code

Just like the multiply function before, the final function takes only one argument, h, but still performs operations on other variables in the scope of functions that have already completed the return. This is possible because of the closure feature.

Translator’s Note: This is so verbose that it feels like the other example is just a repeat. The idea behind currying is to take a function and derive a function that returns a particular function.

The application of Currie in mathematics

I kind of like the math illustration 👉 Wikipedia takes the concept of Currying one step further. Let’s take a closer look at currying with our example. Let’s say I have an equation

f(x,y) = x^2 + y = z
Copy the code

You have two variables, x and y, and if you assign them x=3 and y=4, you get the value of z. Let’s replace the variable values in the function f(x,y) with y=4 and x=3:

F (3,4) = x^2 + y = 3^2 + 4 = 13 = zCopy the code

The result for z is 13 and we can also curlize f of x,y, and provide these variables in a series of functions.

h = x^2 + y = f(x,y)
hy(x) = x^2 + y = hx(y) = x^2 + y
[hx => w.r.t x] and [hy => w.r.t y]
Copy the code

Note: hx represents an identifier with h subscript x, similarly hy represents an identifier with h subscript y. W.R.T, with respect to, is used to take a derivative with respect to, or to satisfy certain conditions and things like that

We set the variable x=3 of the equation f(x,y)=x^2+y, which will return a new equation with y as the variable.

H3 (y) = 3^2 + y = 9 + y Note: H3 indicates the identifier of H with subscript 3Copy the code

It is also equivalent to:

h3(y) = h(3)(y) = f(3,y) = 3^2 + y = 9 + y
Copy the code

The result of the function is still undefined, but it returns a new equation that expects some other variable y, 9+y. Next, we pass in y equals 4

H3 (4) = H (3)(4) = f(3,4) = 9 + 4 = 13Copy the code

The variable y is the last in the chain of variables and is then added to the previously reserved variable x=3. The value is finally resolved and the result is 12. So basically, we reduced the equation f(x,y)=3^2+y to a series of equations before we got to the final result.

3 ^ 2 + y - > 9 + y f (3, y) = h3 (y) = 3 ^ 2 + y = 9 + y f (3, y) = 9 + y f (3, 4) = h3 (4) = 9 + 4 = 13Copy the code

All right, so these are some of the mathematical applications of Curryization, if you think that’s not clear enough. You can read more about it on Wikipedia.

Currie and partially applied functions

Now, some people may begin to think that the number of nested functions of a curlized function depends on the arguments it accepts. Yes, that’s curryification. I can design a Curryization function like volume:

function volume(l) {
    return (w, h) => {
        return l * w * h
    }
}
Copy the code

So, it can be called like this:

const hCy = volume(70);
hCy(203,142);
hCy(220,122);
hCy(120,123);
Copy the code

Or this:

Volume (70) (90, 30); Volume (70) (390320); Volume (70) (940340);Copy the code

We’ve just defined a special function for calculating the volume of any length (l), 70 cylinder. It takes 3 arguments and has 2 levels of nested functions, unlike the previous version which takes 3 arguments and has 3 levels of nested functions. But this version is not Curryized. We just made a partial application of the volume function. Curryization is related to some application functions, but they are different concepts. Partial application functions convert a function to a function with fewer elements (that is, more parameters).

function acidityRatio(x, y, z) {
    return performOp(x,y,z)
}
|
V
function acidityRatio(x) {
    return (y,z) => {
        return performOp(x,y,z)
    }
}
Copy the code

Note: I intentionally did not implement the performOp functions. Because here, this is not necessary. All you need to know is the concept behind currie and partial application functions. This is a partial application of the acidityRatio function, which does not involve curryization. The acidityRatio function is applied to accept fewer elements and fewer parameters than the original function expects. Curryization can be achieved as follows:

function acidityRatio(x) {
    return (y) = > {
        return (z) = > {
            return performOp(x,y,z)
        }
    }
}
Copy the code

Curryization is the creation of nested functions based on the number of arguments to a function, with each function taking one argument. If there are no parameters, there is no currying. There may be a situation where currying and partial applications meet each other. Suppose we have a function:

function div(x,y) {
    return x/y;
}
Copy the code

If partial application forms are written, the result is:

function div(x) {
    return (y) => {
        returnx/y; }}Copy the code

Similarly, currying has the same result:

function div(x) {
    return (y) => {
        returnx/y; }}Copy the code

Although currie and partially applied functions give the same result, they are two different things. As we said before, currying is related in part to the application, but the design is actually quite different. What they have in common is that they both rely on closures.

Is it useful to currie functions?

Of course it does. Currie will come in handy if you want to:

Write small blocks of code that can be easily reused and configured, as we did with NPM:

For example, let’s say you have a store and you want to give a 10% discount to your preferred customers:

function discount(price, discount) {
    return price * discount
}
Copy the code

When a discount customer buys a $500 item, you give him a discount:

Const price = discount,0.10 (500); //$50 
// The $500-$50 = The $450
Copy the code

You can see how, in the long run, we’ll find ourselves calculating a 10% discount every day:

Const price = discount,0.10 (1500); //The $150
// The $1The 500 -The $150 = The $1,350
const price = discount(2000,0.10); // The $200
// $2The 000 -The $200 = The $1, 500 const price = discount(50,0.10); //A $5
// $50 - A $5 = $45Const price = discount,0.10 (5000); //The $500
// A $5The 000 -The $500 = $4,500
const price = discount(300,0.10); // $30
// The $300 - $30 = The $270
Copy the code

We can curify the discount function so that we don’t have to increase the discount by 0.01 each time.

function discount(discount) {
    return (price) => {
        returnprice * discount; }} const tenPercentDiscount = discount(0.1);Copy the code

Now, we can just count the prices of the items your customers buy:

tenPercentDiscount(500); // $50
// The $500 - $50 = The $450
Copy the code

In the same way, some discount customers are more important than some discount customers – let’s call them super customers. And we want to offer a 20% discount to these super customers. We can use our Curryized discount function:

Const twentyPercentDiscount = discount (0.2);Copy the code

We configured a new function for our supercustomer with a currie discount of 0.2 (20%). The returned function twentyPercentDiscount will be used to calculate the discount for our supercustomer:

twentyPercentDiscount(500); / / / / 100The $500 - The $100 = The $400twentyPercentDiscount(5000); / / / / 1000A $5The 000 -The $1, 000 =$4,000 twentyPercentDiscount(1000000); / / / / 200000The $1The 000000 -The $200, 000 =The $600, 000,Copy the code

2. Avoid frequent calls to functions with the same arguments

For example, we have a function that calculates the volume of a cylinder

function volume(l, w, h) {
    return l * w * h;
}
Copy the code

It happens that all the cylinders in the warehouse are 100 meters high, and you will see that you will call this function repeatedly, h is 100 meters

Volume (200,30,100) / / 2003000 l volume (32,45,100); / / / / 144000 l volume (2322232100) 53870400 lCopy the code

To solve this problem, you can curify the volume function (as we did before) :

function volume(h) {
    return (w) => {
        return (l) => {
            return l * w * h
        }
    }
}
Copy the code

We can define a function that specifies the height of the cylinder:

const hCylinderHeight = volume(100); hCylinderHeight(200)(30); / / 600000 l hCylinderHeight (2322) (232); / / 53870400 lCopy the code

A general curryization function

Let’s develop a function that takes any function and returns a curlized version of the function. To do that, we’ll have this (though your approach may not be the same as mine) :

functioncurry(fn, ... args) {return(... _arg) => {returnfn(... args, ... _arg); }}Copy the code

What does the above code do? The curry function takes a function (fn) that we want to curry and a variable number of parameters (… The args). The remaining operation is used to collect the number of parameters after fn to… The args. Then, return a function that similarly collects the remaining arguments as… The args. This function calls the original function fn by passing in the spread operator as an argument… The args and… Args, then returns the value to use. Now we can use the Curry function to create specific functions. Let’s use the Curry function to create more specific functions to calculate the physical exam (one of which is to calculate the volume of a cylinder at 100 meters tall)

function volume(l,h,w) {
    returnl * h * w } const hCy = curry(volume,100); HCy (200900); / / 18000000 l hCy (70 '); // 420000lCopy the code

conclusion

Closures make JavaScript currying possible. The ability to preserve the state of functions that have been executed enables us to create factory functions – functions that can add specific values to their arguments. Currying, closures, and functional programming are tricky. But I can assure you, with time and practice, you’ll begin to master it and see how valuable it is.

reference

Currie – Wikipedia part of the Application function (end)

Afterword.

The above translation is only used for learning communication, the level is limited, there are inevitably mistakes, please correct me.

The original

Blog. Bitsrc. IO/understandi…