ref – https://hackernoon.com/currying-in-js-d9ddc64f162e
https://codeburst.io/currying-in-javascript-ba51eb9778dc
https://blog.bitsrc.io/understanding-currying-in-javascript-ceb2188c339
https://dev.to/damcosset/higher-order-functions-in-javascript-4j8b
Kyle Simpsons – Functional Programming
Arity
Higher Order functions.
Closure
Curry – the process (implementing) of taking in a function with multiple parameters and creating unary (takes in 1 parameter) closures/functions.
Partial App – is applying the params and actually using the app.
Partial Functions
ref – https://medium.com/@JosephJnk/partial-function-application-in-javascript-and-flow-7f3ca87074fe
Partial application is the act of taking a function which takes multiple arguments, and “locking in” some of those arguments, producing a function which takes fewer arguments. For instance, say we have this function:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
function power(exponent, base) { console.log('execute function power, exponent:' + exponent + ', ' + ' base: ' + base); let result = 1; for (let i = 0; i < exponent; i++) { result = result * base; } return result; } var square = function(x) { console.log('var square references unnamed function expression.'); console.log('parameter x is ' + x); return power(2, x); } |
‘square’ is partial application: we create square by making a new function, which just passes its arguments through to another function, adding some hard-coded arguments to that other function.
Partial application transforms a function into another function with smaller arity.
where arity means the number of arguments a function takes.
Currying
Named after Haskell Brooks Curry, currying is the process of breaking down a function into a series of functions that each take a single argument. In other words, Currying is a technique of evaluating function with multiple arguments, into sequences of function with single argument.
In other words, when a function, instead of taking all arguments at one time, takes argument one and returns a new function. that takes argument two and returns a new function which takes the third one, and so forth, until all arguments have been fulfilled.
That is, when we turn a function call add(1,2,3) into add(1)(2)(3) . By using this technique, the little piece can be configured and reused with ease.
A curried function is a function that takes multiple arguments one at a time. Given a function with 3 parameters, the curried version will take argument one and return a function that takes argument two. It then returns a function that takes the third argument. The last function returns the result of applying the function to all of its arguments.
Why it’s useful ?
Currying helps you to avoid passing the same variable again and again.
It helps to create a higher order function. It’s extremely helpful in event handling.
Little pieces can be configured and reused with ease.
Currying example
Here’s a simple example. We’ll write a function sum3 takes three numbers and returns their sum.
In a standard way, the function is implemented like so:
1 2 3 4 |
function sum3(x, y, z) { return x + y + z; } console.log(sum3(1, 2, 3) // 6 |
However, if we provide too few parameters, or too many parameters, then it can have unintended consequences.
add(1,2) –> NaN
add(1,2,3,4) –> 6 //Extra parameters will be ignored.
The curried version of sum3 behaves a differently. It accepts one argument and returns one function. The returned function also accepts one argument and also returns another function that also accepts one argument and …
This cycle continues until the returned function accepts the last argument. The last one in the chain, finally returns the sum.
1 2 3 4 5 6 7 8 |
function sum3(x) { return (y) => { return (z) => { return x + y + z; }; }; } console.log(sum3(1)(2)(3)) // 6 |
This works because JS supports closures.
Currying is a transform that makes f(a,b,c) callable as f(a)(b)(c). JavaScript implementations usually both keep the function callable normally and return the partial if arguments count is not enough.
Another example of why currying is useful
Of course, currying comes in handy when you want to:
1. Write little code modules that can be reused and configured with ease, much like what we do with npm:
For example, you own a store🏠 and you want to give 10%💵 discount to your fav customers:
1 2 3 |
function discount(price, discount) { return price * discount } |
When a fav customer buys a good worth of $500, you give him:
1 2 |
const price = discount(500,0.10); // $50 // $500 - $50 = $450 |
You see that in the long run, we would find ourselves calculating discount with 10% on a daily basis.
1 2 3 4 5 6 7 8 9 10 |
const price = discount(1500,0.10); // $150 // $1,500 - $150 = $1,350 const price = discount(2000,0.10); // $200 // $2,000 - $200 = $1,800 const price = discount(50,0.10); // $5 // $50 - $5 = $45 const price = discount(5000,0.10); // $500 // $5,000 - $500 = $4,500 const price = discount(300,0.10); // $30 // $300 - $30 = $270 |
We can curry the discount function, so we don’t always add the 0.10 discount:
1 2 3 4 5 6 |
function discount(discount) { return (price) => { return price * discount; } } const tenPercentDiscount = discount(0.1); |
Now, we can now calculate only with price of the goods bought by your fav customers:
1 2 |
tenPercentDiscount(500); // $50 // $500 - $50 = $450 |
Again, it happens that, some fav customers are more important than some fav customers- let’s call them super-fav customers. And we want to give 20% discount to our super-fav customers.
We use our curried discount function:
1 |
const twentyPercentDiscount = discount(0.2); |
We setup a new function for our super-fav customers by calling the curry function discount with a 0.2 value , that is 20%.
The returned function twentyPercentDiscount will be used to calculate discounts for our super-fav customers:
1 2 3 4 5 6 |
twentyPercentDiscount(500); // 100 // $500 - $100 = $400 twentyPercentDiscount(5000); // 1000 // $5,000 - $1,000 = $4,000 twentyPercentDiscount(1000000); // 200000 // $1,000,000 - $200,000 = $600,000 |
2. Avoid frequently calling a function with the same argument:
For example, we have a function to calculate the volume of a cylinder:
1 2 3 4 5 6 7 8 9 10 11 |
function volume(l, w, h) { return l * w * h; } <pre> It happens that all the cylinders in your warehouse🏠 are of height 100m. You will see that you will repeatedly call this function with h as 100: <pre> volume(200,30,100) // 2003000l volume(32,45,100); //144000l volume(2322,232,100) // 53870400l |
To resolve this, you curry the volume function(like we did earlier):
1 2 3 4 5 6 7 |
function volume(h) { return (w) => { return (l) => { return l * w * h } } } |
We can define a specific function for a particular cylinder height:
1 2 3 |
const hCylinderHeight = volume(100); hCylinderHeight(200)(30); // 600,000l hCylinderHeight(2322)(232); // 53,870,400l |
More Example
1 2 3 4 5 6 7 8 9 |
function greet(greeting) { // return anonymous function and pass array element in as name. 'Nate', 'Jim', ....etc return function(name) { return `${greeting} ${name}`; } } const friends = ['Nate', 'Jim', 'Scott', 'Dean']; const friendGreetings = friends.map(greet); |
How does Curry work?
Currying works by natural closure.The closure created by the nested functions to retain access to each of the arguments.So inner function have access to all arguments.
Additional Examples
ref – https://www.sitepoint.com/currying-in-functional-javascript/
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
var greet = function(greeting, name) { console.log(greeting + ", " + name); }; //greet("Hello", "Heidi"); //"Hello, Heidi" var greetCurried = function(greeting) { return function(name) { console.log(greeting + ", " + name); }; }; greetCurried("hellllo")('ricky'); var greetHello = greetCurried("Hello"); greetHello('ricky'); greetHello('dawn'); |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
// we can create new custom functions to greet //as many people as we choose in as many ways as suits our purposes var greetDeeplyCurried = function(greeting) { return function(separator) { return function(emphasis) { return function(name) { console.log(greeting + separator + name + emphasis); }; }; }; }; var greetAwkwardly = greetDeeplyCurried("Hello")("...")("?"); greetAwkwardly('brutus'); var greetAwkwardly2 = greetDeeplyCurried("Yooo")(", ")("!"); greetAwkwardly2('rick'); var greetAwkwardly3 = greetDeeplyCurried("uhhh")("......"); var valleyGirlGreet = greetAwkwardly3("?"); valleyGirlGreet("wuuuuut"); valleyGirlGreet("may i help you"); valleyGirlGreet("like totally"); |
You can see how powerful this approach is, especially if you need to create a lot of very detailed custom functions.
The only problem is the syntax. As you build these curried functions up, you need to keep nesting returned functions,
and call them with new functions that require multiple sets of parentheses, each containing its own isolated argument.
It can get messy.
To address that problem, one approach is to create a quick and dirty currying function that will take
the name of an existing function that was written without all the nested returns.
A currying function would need to pull out the list of arguments for that function,
and use those to return a curried version of the original function:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
var greeter = function(greeting, separator, emphasis, name) { console.log("-- greeter --"); console.log(greeting + separator + name + emphasis); }; // uncurried the reference to function 'greeter' var curryIt = function(uncurried) { console.log('--- curryIt ---'); //we passesd in curryIt(greeter, "Hello", ", ", ".") as the parameter...hence let's log it console.log(arguments); // function, "Hello", ", ", "." // so what we do here with the slice is to get an sub-array from starting index 1 on our arguments array // the reason we start from index 1 is because we're not interested in the function. We want the rest. var parameters = Array.prototype.slice.call(arguments, 1); console.log(parameters); // "Hello", ", ", "." // return a function for others to use return function() { console.log('--- Curried function ---'); // Heidi // Eddie console.log(arguments); // through closure, we take the existing parameters from the parent scope, then concat further arguments var concatParams = parameters.concat(Array.prototype.slice.call(arguments, 0)); console.log(concatParams); //["Hello", ",", ".", "Heidi"]; // The apply() method calls function greeter with window as this value, and arguments provided as an array console.log('call greeter, with window as this object, and arguments as array'); return uncurried.apply(this, concatParams); }; }; var greetHello = curryIt(greeter, "Hello", ", ", "."); greetHello("Heidi"); //"Hello, Heidi." greetHello("Eddie"); //"Hello, Eddie." |
Currying is an incredibly useful technique from functional JavaScript.
It allows you to generate a library of small, easily configured functions that behave consistently,
are quick to use, and that can be understood when reading your code. Adding currying to your coding practice
will encourage the use of partially applied functions throughout your code, avoiding a lot of potential repetition,
and may help get you into better habits about naming and dealing with function arguments.
Higher Order function
A higher order function is a function that takes a function as an argument, or returns a function.
One of the great advantages of using higher order functions when we can is composition.
We can create smaller functions that only take care of one piece of logic. Then, we compose more complex functions by using different smaller functions.
This technique reduces bugs and makes our code easier to read and understand.
By learning to use higher-order functions, you can start writing better code.
First we see an array of students:
1 2 3 4 5 6 7 8 9 10 11 12 |
var grades = [ {name: 'John', grade: 8, sex: 'M'}, {name: 'Sarah', grade: 12, sex: 'F'}, {name: 'Bob', grade: 16, sex: 'M'}, {name: 'Johnny', grade: 2, sex: 'M'}, {name: 'Ethan', grade: 4, sex: 'M'}, {name: 'Paula', grade: 18, sex: 'F'}, {name: 'Donald', grade: 5, sex: 'M'}, {name: 'Jennifer', grade: 13, sex: 'F'}, {name: 'Courtney', grade: 15, sex: 'F'}, {name: 'Jane', grade: 9, sex: 'F'} ] |
Let’s write functions:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
let isBoy = student => student.sex === 'M' let isGirl = student => student.sex === 'F' let getBoys = grades => ( grades.filter(isBoy) ) let getGirls = grades => ( grades.filter(isGirl) ) let average = grades => ( grades.reduce((acc, curr) => ( acc + curr.grade ), 0) / grades.length ) let maxGrade = grades => ( Math.max(...grades.map(student => student.grade)) ) let minGrade = grades => ( Math.min(...grades.map(student => student.grade)) ) |
note, interface for reduce:
function(total,currentValue, index,arr) Required.
A function to be run for each element in the array.
Function arguments:
total Required. The initialValue, or the previously returned value of the function
currentValue Required. The value of the current element
index Optional. The array index of the current element
arr Optional. The array object the current element belongs to
Then insert other functions into function average. It helps you get the average of boy’s grades, and girls grades, and everyone’s grades.
Hence as you can see, its composition.
1 2 3 |
let classroomAverage = average(grades) // 10.2 let boysAverage = average(getBoys(grades)) // 7 let girlsAverage = average(getGirls(grades)) // 13.4 |
udemy example
Given function ffn that adds three args.
1 2 3 |
const ffun = function(a,b,c) { return a+b+c; } |
We create a curry function that will will spit back a closure with a function reference in it depending on whether you keep executing the curried function.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
function curry(fn,arity = fn.length) { // length is # of args return (function nextCurried(prevArgs){ // IFFE with empty array as prevArgs so that // we create a closure // that way, our curried function inside has access to prevArgs return function curried(nextArg) { var args = [ ...prevArgs, nextArg ]; // prevArgs accessed via Closure if (args.length >= arity) { return fn( ...args ); // calls the original fn with these args spread onto the parameter } else { return nextCurried( args ); // returns a closured function reference. } }; })( [] ); } |
It will be used like this:
1 2 3 |
const curried = curry(ffun); const result = curried(10)(43)(22); console.log('result', result); |
Let’s break it down.
We start off with
1 2 3 4 |
function curry(fn,arity = fn.length) { } |
Which dictates the function and the # of args. The # of args will be matched later as each arg is executed during the curry. Hence, take note of arity
Now we create an IIFE that executes right away. This gives prevArgs to whatever is inside. If we decide to return a function that is inside, then that function has access to prevArgs.
1 2 3 |
return (function nextCurried(prevArgs){ // IFFE with empty array as prevArgs so that // can access prevArgs })( [] ); |
Why do we do this? Because we want to get the previous args. Initially, we start off with empty array [].
1 2 3 4 5 6 7 8 9 10 11 12 13 |
return (function nextCurried(prevArgs){ // IFFE with empty array as prevArgs so that // we create a closure // that way, our curried function inside has access to prevArgs return function curried(nextArg) { var args = [ ...prevArgs, nextArg ]; // prevArgs accessed via Closure if (args.length >= arity) { // success case, call original function return fn( ...args ); // calls the original fn with these args spread onto the parameter } else { // not there yet case return nextCurried( args ); // returns a closured function reference. } }; })( [] ); |
curried is a function reference, so we need to execute and pass in a parameter. After we pass it in, we will be able to see the full args list.
For example curry(10)(14).
We have empty array to start when we execute curry. Then we return a function reference curried,
curry(10)(14), in which we put 10.
Now, we have
1 |
var args = [10] |
the # of args !== arity yet, so we keep going.
We create another closure with IIFE with the args list. We return a function reference curried,
curry(10)(14), in which we put 14.
Now, we have
1 |
var args = [10, 14] |
so on and so forth…
Finally, when the # of args we input matches that of arity, we execute fn, which is the original function.
Source
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
const ffun = function(a,b,c) { return a+b+c; } function curry(fn,arity = fn.length) { // length is # of args return (function nextCurried(prevArgs){ // IFFE with empty array as prevArgs so that // we create a closure // that way, our curried function inside has access to prevArgs return function curried(nextArg) { var args = [ ...prevArgs, nextArg ]; // prevArgs accessed via Closure if (args.length >= arity) { return fn( ...args ); // calls the original fn with these args spread onto the parameter } else { return nextCurried( args ); // returns a closured function reference. } }; })( [] ); } const curried = curry(ffun); const result = curried(10)(43)(22); console.log('result', result); |