Categories
Codes Javascript

What Is Currying?

“People need to repeat data… and sense repeation… If I spend my time for that shit… I am going to waste my life, I just want sense new and genuine stuff.”

― Deyth Banger, Jokes From A

Introduction

No, I do not share on how to cook curry with JavaScript. That might be possible in the future, but this post is not about that.

You might hear some programmers talk about currying, and you are too shy to ask because you feel you would look stupid by asking a probably basic question. Tell you what, I did it on a job interview before, and you guessed it: I did not get the job.

What is currying?

“… currying is the technique of converting a function that takes multiple arguments into a sequence of functions that each take a single argument.”

Wikipedia

Yes, it definitely is not about cooking curry.

Here is a simple example of currying:

const add = (a) => {
  return (b) => {
    return a + b;
  };
};

console.log(add(4)(5)); // 9

What’s going on here? Let’s break the functions:

First of all, add() is a function that has the parameter a.

const add = (a) => {
  ...
}

Calling add() will return another function, which is this:

...
  (b) => {
    return a + b;
  };
...

The function has a parameter b. When this function is called, it will return the result of a + b. We get a from the first function, then b from the second function. This example shows you the longer way to call the function:

const add = (a) => {
  return (b) => {
    return a + b;
  };
};

const firstFunc = add(4);
console.log(firstFunc);
/* 
  (b) => {
    return a + b;
  }
*/
console.log(firstFunc(5)); // 9

Calling add(4) will return a function called firstFunc, which can be called to get the final result.

By the way, you can also write the function above in its shortest form using most of the ES6 syntax:

const add = a => b => a + b;

First Class Functions

It’s an important concept of programming in JavaScript, and you should understand it first when you learn about currying. To put it simply, JavaScript supports functions as an argument and a return value. Here is an example:

const helloThere = () => console.log ("Hello there!");

const funcWithFuncAsParam = (func) => func(); 
// Hello there!

const funcWhichReturnsFunc = () => helloThere;

funcWithFuncAsParam(funcWhichReturnsFunc());
// Hello there!

funcWhichReturnsFunc() will return a function. Note that the function is simply returned, and not executed yet. The function will be executed when funcWithFuncAsParam() is called.

Practical Application of Currying

Alright, we know what currying is and that we know it’s pretty cool. Next question is, how do we implement it in our code?

Partial Application

It means that a function in which some of its arguments are already fixed in its scope, but not all of them. In other words, by using a curried function, we can create a specialized function for the undecided arguments without repeating the code.

A curried function will return a partial application, but a partial application might not be a result of a curried function.

Here is an example:

const greet = input => name => {
  console.log(input + ", " + name);
};

// the partial applications
const hello = greet('Hello');
const hey = greet('Hey');

// the specialized functions
hello('Andy'); // Hello, Andy
hello('Mary'); // Hello, Mary
hello('John'); // Hello, John
hey('Jack'); // Hey, Jack
hey('Jill'); // Hey, Jill

// the customized function
greet('He\'s dead')('Jim'); // He's dead, Jim

The function hello() will always have the value from the variable input as "Hello". Same goes with hey(). The arguments for hello() and hey() are undecided when creating the functions. Therefore hello() and hey() are partial applications.

Then, we use the partial application to greet a name. The name could be anything. But the greet will always be “Hello” for hello() and “Hey” for hey().

Lastly, the customized function greet('He\'s dead')('Jim'), it’s called when we need to use the other greetings beside “Hello” and “Hey”.

Functional Composition

Functional composition is a way to run combined multiple functions as one function only. For example see the snippet below:

const plusOne = n => n+1;
const plusTwo = n => n+2;
const plusFour = n => n+4;

const compose = (...fns) => n => {
  return fns.reduce((total, fn) => fn(total), n);
};

const plusThree = compose(plusOne, plusTwo);
const plusSix = compose(plusTwo, plusFour);
const plusSeven = compose(plusOne, plusTwo, plusFour);

console.log(plusThree(2)); // 5
console.log(plusSix(2)); // 8
console.log(plusSeven(2)); // 9

The coolest thing is that it only needs one argument the execute all the functions within a composed function. plusThree(), plusSix(), plusSeven() are composed functions.

So, what happens in the compose function?

First, it accepts any arguments (...fns) to create a function with parameter n.

const compose = (...fns) => 
   { /* function with param n */ }

// if we run plusThree,
// fns = [plusOne, plusTwo];

Second, the function with parameter n will run reduce function with argument n as the initial value.

n => {
  return fns.reduce(/* function */, /* initialValue */);
};

// result composed function: 
// plusThree, plusSix, plusSeven

Third, for every function fn iterated in the reduce function, it will call fn(). It means running the function fn() with argument total on it’s turn. The initial value of total will be filled with n. Then fn() will return the result. An example for more details: on composed function plusOneTwo, plusOne and plusTwo are fn.

fns.reduce((total, fn) => fn(total), n);

/* plusThree(2)
fns = [plusOne, plusTwo];
total -> 2 // initial value = n
*/

Fourth, The result from fn() will be returned as total, which will be the argument for the next iteration of fns.

(total, fn) => fn(total)

1st iteration:
fn -> plusOne(2)
total -> 3

2nd iteration:
fn -> plusTwo(3) // 3 is taken from total
total -> 5

Finally, the composed function will return the result after completing all iterations.

return fns.reduce(...);
// plusThree(2) will return: 5

Conclusion

Well, that’s all about currying. It can retain the state of the functions already executed, and only need adding specific value as the argument of the created functions.

It’s pretty cool but might seem intimidating if you just learn about this. Just keep on practicing, and someday you will surely know when to use currying perfectly in real-life cases.

Let me know about your experience. What cases you manage to solve elegantly with currying?

Keep on practicing => live your code => code your life;

“Even if yesterday was wildly successful, I still don’t want to repeat it. Rather, I want to build on it.”

― Craig D. Lounsbrough

By Ericko Yap

Just a guy who is obsessed to improve himself. Working as a programmer in a digital banking company. Currently programming himself in calisthenics, reading books, and maintaining a blog.

Leave a Reply

Your email address will not be published. Required fields are marked *