Currying in JavaScript
Miscellaneous: Currying
What is currying in JavaScript?
View Answer:
function curry(f) {
// curry(f) does the currying transform
return function (a) {
return function (b) {
return f(a, b);
};
};
}
// usage
function sum(a, b) {
return a + b;
}
let curriedSum = curry(sum);
console.log(curriedSum(1)(2)); // 3
What is the main rule of currying functions in JavaScript?
View Answer:
function curry(func) {
return function curried(...args) {
if (args.length >= func.length) {
return func.apply(this, args);
} else {
return function (...args2) {
return curried.apply(this, args.concat(args2));
};
}
};
}
function sum(a, b, c) {
return a + b + c;
}
let curriedSum = curry(sum);
console.log(curriedSum(1, 2, 3)); // 6, still callable normally
console.log(curriedSum(1)(2, 3)); // 6, currying of 1st arg
console.log(curriedSum(1)(2)(3)); // 6, full currying
Why is currying useful?
View Answer:
What is a partial application?
View Answer:
Let's take the example of a function that adds three numbers. We'll implement it in JavaScript first without currying, and then with currying.
Without currying:
function add(a, b, c) {
return a + b + c;
}
console.log(add(1, 2, 3)); // Outputs: 6
With currying:
function add(a) {
return function(b) {
return function(c) {
return a + b + c;
}
}
}
console.log(add(1)(2)(3)); // Outputs: 6
In the curried version, each function call returns another function that takes the next argument, until all arguments have been provided and the final value can be computed.
Now, let's look at an example of partial application using the curried function.
let addOne = add(1);
let addOneAndTwo = addOne(2);
console.log(addOneAndTwo(3)); // Outputs: 6
In this example, we created a new function addOne
by calling add(1)
. This function takes two arguments and adds one to the sum of those arguments. We then partially applied addOne
again to create addOneAndTwo
, which adds one and two to any number it's given.
Can you curry built-in JavaScript functions?
View Answer:
Yes, you can curry built-in JavaScript functions. Let's take the built-in Math.pow()
function as an example. This function takes two arguments, the base and the exponent, and returns the base raised to the power of the exponent.
Here's how you can create a curried version of Math.pow()
:
function curryPow(base) {
return function(exponent) {
return Math.pow(base, exponent);
};
}
const square = curryPow(2);
console.log(square(3)); // Outputs: 8
In this example, we've curried the Math.pow()
function so that it takes its arguments one at a time. We've then created a new function square
that squares a number by partially applying curryPow
with the base set to 2.
But the above example only works for a function with two arguments. If you want to curry functions with arbitrary numbers of arguments, you need a more general currying function. Here's a simple example of how you can implement one:
function curry(fn) {
return function curried(...args) {
if (args.length >= fn.length) {
return fn.apply(this, args);
} else {
return function(...args2) {
return curried.apply(this, args.concat(args2));
};
}
};
}
const curriedPow = curry(Math.pow);
const square = curriedPow(2);
console.log(square(3)); // Outputs: 8
In this example, curry
is a higher-order function that takes a function fn
and returns a new function. If this new function is called with enough arguments, it calls fn
with those arguments. Otherwise, it returns a new function that expects the rest of the arguments. This allows you to curry functions with any number of arguments.
How does currying relate to functional programming?
View Answer:
What is the Arity of a function?
View Answer:
Can currying impact performance?
View Answer:
How does currying differ from function composition?
View Answer:
Currying is a process in functional programming where a function with multiple arguments is transformed into a sequence of functions, each with a single argument. The main benefit of currying is that it allows for partial application of functions, which can make code more modular and easier to reuse.
Here's an example of a curried function in JavaScript:
function add(a) {
return function(b) {
return a + b;
};
}
const addFive = add(5);
console.log(addFive(10)); // Outputs: 15
In this example, add
is a curried function that takes two arguments one at a time. We partially apply add
to create a new function addFive
that adds five to its argument.
Function composition, on the other hand, is a technique where you create a new function by composing two or more functions, meaning you use the output of one function as the input of another.
Here's an example of function composition in JavaScript:
function addFive(x) {
return x + 5;
}
function double(x) {
return x * 2;
}
function compose(f, g) {
return function(x) {
return f(g(x));
};
}
const doubleThenAddFive = compose(addFive, double);
console.log(doubleThenAddFive(10)); // Outputs: 25
In this example, doubleThenAddFive
is a new function created by composing addFive
and double
. It doubles its argument and then adds five to the result.
How does currying work with higher-order functions?
View Answer:
Let's consider an example of a higher-order function, a simple filter
function:
function filter(arr, testFunc) {
let result = [];
for (let i = 0; i < arr.length; i++) {
if (testFunc(arr[i])) {
result.push(arr[i]);
}
}
return result;
}
In the filter
function, arr
is an array and testFunc
is a function that tests each element of the array. The filter
function returns a new array that includes only the elements for which testFunc
returns true.
Now, let's curry the filter
function:
function curriedFilter(arr) {
return function(testFunc) {
let result = [];
for (let i = 0; i < arr.length; i++) {
if (testFunc(arr[i])) {
result.push(arr[i]);
}
}
return result;
}
}
let arr = [1, 2, 3, 4, 5, 6];
let filterArr = curriedFilter(arr);
let getEvenNumbers = filterArr(num => num % 2 === 0);
console.log(getEvenNumbers); // Outputs: [2, 4, 6]
Here, the curriedFilter
function is a curried version of filter
. It first takes an array and then returns a function that takes a test function. You can see how the curried function allows us to create a specialized function getEvenNumbers
from the general filter
function. This is how currying works with higher-order functions in practice.
What is point-free style in functional programming?
View Answer:
Here's an example of point-free style in JavaScript:
const add = a => b => a + b;
const increment = add(1);
const double = a => a * 2;
const doubleThenIncrement = x => increment(double(x));
We can define doubleThenIncrement
in a point-free style by removing the argument x
:
const doubleThenIncrement = compose(increment, double);
In this example, we're using a compose
function, which is a common helper function in functional programming:
function compose(f, g) {
return function(x) {
return f(g(x));
};
}
In the point-free version of doubleThenIncrement
, the function is defined entirely in terms of other functions, without ever mentioning the arguments those functions are applied to. This is characteristic of point-free style.
Can you use currying with async functions in JavaScript?
View Answer:
Here's an example of a curried async function...
const asyncAdd = a => async b => a + b;
(async function() {
const addFive = asyncAdd(5);
console.log(await addFive(10)); // Outputs: 15
})();
In this example, asyncAdd
is an async function that takes two arguments one at a time, and addFive
is a new async function that adds five to its argument. When you call addFive(10)
, it returns a Promise that eventually resolves to 15
.
Note that you need to use the await
keyword when calling addFive
, because it's an async function and you want to wait for the Promise it returns to resolve. Also, remember that the top-level async/await syntax is only allowed inside an async function, hence the use of an Immediately Invoked Function Expression (IIFE) in this example.
How does currying compare to using default parameters in JavaScript?
View Answer:
Currying:
function add(a) {
return function(b) {
return a + b;
};
}
console.log(add(2)(3)); // Outputs: 5
Default Parameters:
function greet(name = 'Guest') {
console.log(`Hello, ${name}!`);
}
greet(); // Outputs: Hello, Guest!
greet('John'); // Outputs: Hello, John!