A B C of JavaScript Currying

Featured on Hashnode
A B C of JavaScript Currying

Welcome back πŸ‘‹, my gorgeous friend on the internet.

Today we will be learning the almighty currying in JavaScript, but before we continue, I assume you are here because you have written your first line of code in JavaScript and already have basic knowledge of what functions are in JavaScript.


Announcement break

Happy to announce that I have joined the freecodecamp Author Community as one of their technical writers, thanks to my readers, the Hashnode team for providing an enabling environment to get better at writing and to all other writers on Hashnode for their amazing energy and feedbacks.

image.png

Get ready to read my first article on freecodecamp very soon.


Welcome back

Because I want you to follow along well and learn from this article, I have gotten 2 reader-friendly articles on JavaScript functions from the Hashnode community and I will advise you to check them out and then come back to read more about the ABC of JavaScript currying.

ARTICLES ON JAVASCRIPT FUNCTION

  1. What is JavaScript function? by Adedotun Adedigba

  2. JavaScript Functions by Bello

Let's do it since you know about javascript function

Now that we are on the same page, let's do this by firstly looking into what JavaScript Currying is actually all about and why you should care about it.


Also at the end of this article, you will;

  1. Have a basic understanding of how JavaScript currying works

  2. Complete a simple use case application of JavaScript currying, and

  3. Finally you won't forget what Javascript currying is 😎

junior-ranger-airplane.gif


What is Currying?

Currying is a process in functional programming in which we can transform a function with multiple arguments into a sequence of nesting functions. It returns a new function that expects the next argument inline.

Don't be confused yet, I have broken the above definition into two parts below which we are going to pick one after the other.

In this article, we will term currying as

  1. A function returning a new function and

  2. A process of converting a function with multiple arguments into a sequence of nesting functions.


1. A function returning a new function

It returns a new function that expects the next argument inline.
(Source: Definition above)

We should know by now that a function is a block of codes that are used to perform an action, which returns a value.

Example 1

// Function that returns a blog url
function getBlogUrl (){
// Blog url
const blogUrl = "https://www.unclebigbay.com";
// getBlogUrl return statement to the caller
return blogUrl;
};

// The caller display the value returned from getBlogUrl and it's data type to the screen
document.write(getBlogUrl() + " - " + typeof(getBlogUrl()))

Output

image.png

In the above example, we have a function named getBlogUrl whose job is to return a string value of blogUrl as a response to it caller, the caller in this example is the document.write(getBlogUrl()).

This means that whenever we call the getBlogUrl function the return response we get from the function will always be the value of blogUrl which is a string data type.

Point: the function getBlogUrl is returning astring value data type


Example 2

Let say we have the function below

// Function that adds two numbers together
function add2Numbers (number1, number2){
    // Add the two given numbers and store in answer
    const answer = number1 + number2;
    // Return the answer to the caller
    return answer;
};

// The caller
document.write(add2Numbers(1, 2) + " - " + typeof(add2Numbers(1, 2)))

Output

image.png

In this example 2, we have a function add2Numbers that takes in two parameters number1 and number2, and then return the sum of the two arguments passed from the caller.

The main thing to observe from this function is the data type of the returned value from the add2Numbers function which is the Number type.

Point: the function add2Numbers is returning aNumber value data type

To this point, we have seen that a function can return a String and Number data type as its value when been invoked, now let's see how we can make a function return a function as its value.

Note: function call === invoke function


Example 3

Let's take a look at this example 3 below

// A function that returns a function
function xFunction(){
// Function being returned to the caller
    return function(){
     // We will do something here later
    };
};

// The caller
document.write(xFunction() + " - " + typeof(xFunction()));

Output

image.png

The xFunction returns the inner function as a plain text because the inner function is not invoked by the caller, so the return statement of the xFunction treats the inner function as a plain text but its data type is a function as we can see in the output above (JavaScript is weird).

So how do we call the inner function? πŸ€”

But wait, in the real application of the currying function, you won't be calling the curried function (xFunction in this case) without calling the inner function, so don't be worried about the plain text function been returned.

Note: inner function === nested function

To draw the point that we are actually returning a function, let's update Example 4 with the code below

// add () to the xFuntion()
document.write(xFunction()() + " - " + typeof(xFunction()()));

Output

image.png

Now we have a function returning a function, thanks for reading, the end!!!

image.png

No, I was just kidding πŸ˜„, we are still together on this, I know by now you are probably wondering "hey unclebigbay why did we have undefined as the returned value and data type on the screen?πŸ€·β€β™‚οΈ

But did you notice we did not return any value from the inner function? πŸ’β€β™‚οΈ

return function(){
     // We will do something here later
};

Remember now? The undefined on the screen is a value we get because the inner function does not return any value.

This is 100% normal because the default returned value of a function is undefined and the datatype of undefined is also undefined

console.log(typeof undefined) // undefined

The extra bracket () we added to the xFunction signifies that the xFunction has a function within it which we want to execute.

Now let's return an actual value from the inner function

Example 4

function xFunction(){
    return function(){
        // Return the sum of 1 and 1 to the caller
        return 1 + 1
    };
};

// The caller
document.write(xFunction()() + " - " + typeof(xFunction()()));

Output

image.png

The inner function is now returning a value of Number data type

Why does this make sense?

First-class citizens can be treated like every other thing like variable values in JavaScript, just like a citizen of a country.

  1. Objects are first-class citizens in JavaScript and

  2. Functions are Objects

First-class citizenship simply means β€œbeing able to do what everyone else can do.” (Source: Developer Intelligence )


This implies that, if you can return a String and a Number data type as a return value in a function, then a function is qualified to be a returned value also.


This explains why an arrow function is possible in JavaScript, assigning a function to a variable.


Wow, we just covered the definition of currying as a function that returns a new function, let us proceed to complete the second definition.


moving to step 2

2. A process of converting a function with multiple arguments into a sequence of nesting functions.


This is a continuation of where we stopped and what we have been doing in the first definition, but we will dive more deeply into currying itself in this second definition.

Now let us make the returned value from Example 4 dynamic, instead of hardcoding the value 1 + 1, let's make the caller determine what two numbers to add up.

Example 5

Let's update Example 4 with the code below

function xFunction(number1){
    return function(number2){
        // Return the sum of number1 and number2 to the caller
        return number1 + number2
    };
};
// The caller passed 1 to xFunction and 3 to the inner function
document.write(xFunction(1)(3) + " - " + typeof(xFunction()()));

Output

image.png

The above function can be written as

// Regular function
function xFunction(number1, number2){
// Return the sum of number1 and number2 to the caller
        return number1 + number2
};

// The caller
document.write(xFunction(1, 10) + " - " + typeof(xFunction(1, 10)))

Output

image.png


Why Currying is Useful

let's say we need 2 instances of the xFunction one that adds 10 to a given number and another that adds 20 to a given number.

Instead of creating new functions in case of using a regular function like the example below

// 1

// Regular function to increase a given number by 10
function increaseBy10(number){
// Return the result to the caller
     return number + 10;
};
// The caller to increase a given number by 10
document.wrtie(increaseBy10(20)); // 120

// 2

// Regular function to increase a given number by 20
function increaseBy20(number){
// Return the result to the caller
     return number + 20;
};
// The caller to increase a given number by 20
document.write(increaseBy20(100)); // 120

What if we need up to 6 instances of the xFunction? that will result in having 6 bunches of the function block, we can achieve this seamlessly with less line of code by using currying, update the curried xFunction to the code below

Example 6

// Curried function
function xFunction(number1){
    return function(number2){
        // Return the sum of number1 and number2 to the caller
        return number1 + number2
    };
};

// Set the xFunction argument to 10 by default - instance of xFunction
const increaseBy10 = xFunction(10);

// Caller to increase 1 by the ```increaseBy10``` function
document.write(increaseBy10(1) + " - " + typeof(increaseBy10(1)));

Explanation

1. We assigned xFunction a default argument of 10 which will be stored in the Lexical Environment to be accessed by the next function.

This enables nested functions within the xFunction to remember or have access to the upper variables (10 in this case).

2. We stored the returned value (the next function) to a variable named increaseBy10 (citizenship).

xFunction returns the next function(currying) to the increaseBy10 variable

3. We then call the increaseBy10 and passed a value of 1 as an argument to it.

increaseBy10 represents the next function


Example 7

function xFunction(number1){
    return function(number2){
        // Return the sum of number1 and number2 to the caller
        return number1 + number2
    };
};

// Set the xFunction argument to 20 by default - instance of xFunction
const increaseBy20 = xFunction(20);

// Caller to increase 30 by ```increaseBy20``` function
document.write(increaseBy10(30) + " - " + typeof(increaseBy10(30)));

Output

image.png


More examples of nested functions

Example 8

Three (3) nested functions.


// Function that adds 3 given numbers
function sum3Numbers(number1) {
    return function(number2){
        return function(number3) {
            // Return the sum of the 3 given numbers to the caller
            return number1 + number2 + number3
        }
    }
}

// The caller
document.write(sum3Numbers(800)(9)(7) + " - " + typeof sum3Numbers(9)(8)(7))

Output

image.png


Example 9

An instance of sum3Numbers a three (3) nested function.

// Function that adds 3 given numbers
function sum3Numbers(number1) {
    return function(number2){
        return function(number3) {
            // Return the sum of the 3 given numbers to the caller
            return number1 + number2 + number3
        }
    }
}

// Prefilled function 1 and first 2
const provideNumber3Only = sum3Numbers(1)(2)

// The caller
document.write(provideNumber3Only(0) + " - " + typeof provideNumber3Only(0))


Example 10

Real-life use case

// Function that registers a new developer
function newDeveloperRegistration (level){
    return function(stack){
        return function(nickname){
            return function(twitterUrl){
                // Generate developer profile
                developerProfile = (level + " " + stack + " " + nickname + " connect on " + twitterUrl)
                // Return developer profile to the caller
                return developerProfile
            }
        }
    }
}

// Junior level instance
const juniorLevel =  newDeveloperRegistration("Junior");

// Senior level with backend stack instance
const seniorLevelBackend =  newDeveloperRegistration("Senior")("backend");

// Output 1
document.write(`Output 1 <br />`)
document.write(juniorLevel("Frontend Developer")("unclebigbay")("https://www.twitter.com/unclebigbay143"))

// To create space between the outputs
document.write(`<br />`)
document.write(`<br />`)

// Output 2
document.write(`Output 2 <br />`)
document.write(seniorLevelBackend("unclebigbay")("https://www.twitter.com/unclebigbay143"))

Output

image.png

Easy right?


Summary

  1. Currying is the process of breaking a function with multiple arguments into a nested function that returns each of those arguments like converting func(x, y, z) into callable func(x)(y)(z)

  2. Lexical Scope allows the next function to access the value or argument of the previous function.

  3. You can make several instances of a curried function.

  4. Currying helps reduce repeating the same argument that is been passed into a function all over again, by creating an instance of it.

Advanced JavaScript Currying Resources

If you would like to learn more about currying, below are my recommended material to study.

  1. JavaScript Currying Partials - Javascript info
  2. Deep Currying in 7 Mintutes - Freecodecamp

Wow, what a journey, I am glad you made it to the end of this article, if you enjoyed and learned from this article, I will like to connect with you, so you won't miss any of my upcoming articles.

Let's connect on



See you in the next article. Bye Bye πŸ™‹β€β™‚οΈ

image.png

If you like my content, you can also buy me a coffee below.