Imagine the dinosaur feeding system in the park. Each species has its own requirements - type of food, portion size, and feeding time. Instead of providing all parameters every time, we can prepare "pre-configured" functions for a given species. This is exactly what currying does - it transforms a multi-argument function into a sequence of single-argument functions.
Currying is a technique of transforming a function that takes multiple arguments into a sequence of functions, each taking exactly one argument.
1// Regular function with 3 arguments
2function feedDinosaur(species, food, portion) {
3 return species + ' gets ' + portion + 'kg of ' + food;
4}
5feedDinosaur('T-Rex', 'meat', 50);
6
7// Curried version - sequence of single-argument functions
8function feedDinosaurCurried(species) {
9 return function(food) {
10 return function(portion) {
11 return species + ' gets ' + portion + 'kg of ' + food;
12 };
13 };
14}
15feedDinosaurCurried('T-Rex')('meat')(50);
16
17// Same thing with arrow functions (more concise)
18const feedCurried = (species) => (food) => (portion) =>
19 species + ' gets ' + portion + 'kg of ' + food;The real power of currying lies in creating specialized functions:
1const feedCurried = (species) => (food) => (portion) =>
2 ({ species, food, portion, timestamp: Date.now() });
3
4// Specialized functions for species
5const feedRex = feedCurried('T-Rex');
6const feedRexMeat = feedRex('meat');
7
8// Using specialized functions
9feedRexMeat(50); // { species: 'T-Rex', food: 'meat', portion: 50, ... }
10feedRexMeat(30); // { species: 'T-Rex', food: 'meat', portion: 30, ... }
11
12// Can also be used for another species
13const feedBrachio = feedCurried('Brachiosaurus');
14const feedBrachioPlants = feedBrachio('plants');
15feedBrachioPlants(200); // { species: 'Brachiosaurus', food: 'plants', portion: 200, ... }We can write a universal
curry function that automatically transforms any function:1function curry(fn) {
2 return function curried(...args) {
3 if (args.length >= fn.length) {
4 return fn(...args);
5 }
6 return (...moreArgs) => curried(...args, ...moreArgs);
7 };
8}
9
10// Usage
11function createEnclosure(zone, type, capacity, voltage) {
12 return { zone, type, capacity, voltage };
13}
14
15const curriedCreate = curry(createEnclosure);
16
17// Can be called in various ways:
18curriedCreate('A')('carnivore')(5)(10000);
19curriedCreate('A', 'carnivore')(5, 10000);
20curriedCreate('A', 'carnivore', 5)(10000);
21curriedCreate('A', 'carnivore', 5, 10000);
22// All calls produce the same resultPartial application is a related technique - it "freezes" some of a function's arguments, creating a new function with fewer parameters.
1function partial(fn, ...presetArgs) {
2 return (...laterArgs) => fn(...presetArgs, ...laterArgs);
3}
4
5// Incident logging function
6function logIncident(severity, zone, message) {
7 return '[' + severity + '] Zone ' + zone + ': ' + message;
8}
9
10// Specialized loggers
11const logCritical = partial(logIncident, 'CRITICAL');
12const logWarning = partial(logIncident, 'WARNING');
13const logCriticalZoneA = partial(logIncident, 'CRITICAL', 'A');
14
15logCritical('B', 'Fence breach detected');
16// '[CRITICAL] Zone B: Fence breach detected'
17
18logCriticalZoneA('T-Rex escaped!');
19// '[CRITICAL] Zone A: T-Rex escaped!'Although both techniques create specialized functions, they differ in how they work: