Blog#79: Understanding Currying Pattern in Javascript

image.png

Để giúp các bạn có thể nâng cao trình độ tiếng Anh, Blog này mình sẽ viết bằng tiếng Anh.

Mục tiêu sẽ là, sử dụng Technical Document để học Tiếng Anh. Mình sẽ cố gắng sử dụng ngữ pháp và từ vựng đơn giản nhất (level~A1/A2) để giúp các bạn đọc nó dễ dàng hơn.


Hi, I'm Tuan, a Full-stack Web Developer from Tokyo 😊. Follow my blog to not miss out on useful and interesting articles in the future.

In functional programming, the currying pattern is a way of writing functions that allows us to partially apply arguments to a function. This means that we can create a new function by providing some, but not all, of the arguments that the original function expects. This can be useful for creating more specific functions that can be re-used in different parts of our code.

What is Currying?

Currying is the process of taking a function that expects multiple arguments and turning it into a sequence of functions that each expect just one argument. For example, consider the following function that adds two numbers together:

function add(x, y) {
  return x + y;
}

We can curry this function by writing a new function that takes the first argument x and returns a new function that expects the second argument y. Here's how we could do this:

function add(x) {
  return function(y) {
    return x + y;
  }
}

Now, instead of calling add(x, y), we can call add(x)(y). This might seem like a small change, but it allows us to do some interesting things.

Benefits of Currying

There are several benefits to using the currying pattern in our code:

1. Reusable Functions

One of the main benefits of currying is that it allows us to create more specific functions that can be re-used in different parts of our code. For example, consider the following function that multiplies a number by 10:

function multiplyBy10(x) {
  return x * 10;
}

This function is specific to multiplying by 10, so we couldn't use it to multiply by any other number. However, if we curry this function, we can create a more general function that can be used to multiply by any number:

function multiply(x) {
  return function(y) {
    return x * y;
  }
}

const multiplyBy10 = multiply(10);
const multiplyBy5 = multiply(5);

Now we have two specific functions, multiplyBy10 and multiplyBy5, that can be re-used throughout our code.

2. Composability

Another benefit of currying is that it allows us to easily compose new functions from existing ones. For example, consider the following functions that add and multiply numbers:

function add(x, y) {
  return x + y;
}

function multiply(x, y) {
  return x * y;
}

We can use currying to create a new function that first multiplies two numbers and then adds a third number to the result:

function add(x) {
  return function(y) {
    return x + y;
  }
}

function multiply(x) {
  return function(y) {
    return x * y;
  }
}

const multiplyAndAdd = x => y => z => add(multiply(x)(y))(z);

Now we can call multiplyAndAdd(2)(3)(4) to perform the calculation (2 * 3) + 4.

Real-World Examples of Currying in Javascript

Now that we've learned about the basics of currying and its benefits, let's take a look at some real-world examples of how we can use this pattern in our Javascript code.

1. Partial Function Application

One common use case for currying is to create a new function that has some of the arguments pre-filled. This is known as partial function application.

For example, consider the following function that calculates the total cost of an order, including sales tax:

function calculateTotal(price, taxRate) {
  return price + (price * taxRate);
}

If we want to create a new function that calculates the total cost of an order with a specific tax rate, we can use currying to do this:

function calculateTotal(price) {
  return function(taxRate) {
    return price + (price * taxRate);
  }
}

const calculateTotalWithTax = calculateTotal(0.08);

Now we can call calculateTotalWithTax(100) to get the total cost of an order with a price of $100 and a tax rate of 8%.

2. Creating Higher-Order Functions

Another common use case for currying is to create higher-order functions, which are functions that take other functions as arguments or return them as output.

For example, consider the following function that takes an array of numbers and a callback function, and returns a new array with the callback function applied to each element:

function map(array, callback) {
  const newArray = [];
  for (const element of array) {
    newArray.push(callback(element));
  }
  return newArray;
}

We can use currying to create a new function that pre-fills the callback argument:

function map(callback) {
  return function(array) {
    const newArray = [];
    for (const element of array) {
      newArray.push(callback(element));
    }
    return newArray;
  }
}

const mapWithMultiplyBy2 = map(x => x * 2);

Now we can call mapWithMultiplyBy2([1, 2, 3]) to get the array [2, 4, 6].

3. Creating Event Handlers

Currying can also be useful for creating event handlers in React or other Javascript libraries.

For example, consider the following function that handles a click event on a button:

function handleClick(event, id, name) {
  console.log(`Button ${id} with name ${name} was clicked!`);
  // Do something with `event`
}

We can use currying to create a new function that pre-fills the id and name arguments:

function handleClick(id, name) {
  return function(event) {
    console.log(`Button ${id} with name ${name} was clicked!`);
    // Do something with `event`
  }
}

const handleClickWithIdAndName = handleClick(1, 'Submit Button');

Now we can pass handleClickWithIdAndName to the onClick prop of a button, and it will log the appropriate message when the button is clicked.

4. Creating Custom Iterators

Currying can also be useful for creating custom iterators in Javascript.

For example, consider the following function that iterates over an array of numbers and returns the sum:

function sum(array) {
  let total = 0;
  for (const element of array) {
    total += element;
  }
  return total;
}

We can use currying to create a new function that iterates over an array and applies a callback function to each element:

function iterate(callback) {
  return function(array) {
    let total = 0;
    for (const element of array) {
      total += callback(element);
    }
    return total;
  }
}

const sum = iterate(x => x + 1);

console.log('sum([1,2,3])', sum([1,2,3])) // 9

Now we can call sum([1, 2, 3]) to get the sum of the array [2, 3, 4].

Conclusion

In this article, we learned about the currying pattern in Javascript and how it can be used to create more specific, re-usable functions and improve the composability and readability of our code. We also looked at several real-world examples of how this pattern can be applied in practical situations. While currying may take some time to get used to, it can be a powerful tool in a functional programming workflow.

As always, I hope you enjoyed this article and learned something new. Thank you and see you in the next articles!

If you liked this article, please give me a like and subscribe to support me. Thank you. 😊

NGUYỄN ANH TUẤN

Xin chào, mình là Tuấn, một kỹ sư phần mềm đang làm việc tại Tokyo. Đây là blog cá nhân nơi mình chia sẻ kiến thức và kinh nghiệm trong quá trình phát triển bản thân. Hy vọng blog sẽ là nguồn cảm hứng và động lực cho các bạn. Hãy cùng mình học hỏi và trưởng thành mỗi ngày nhé!

Đăng nhận xét

Mới hơn Cũ hơn