We use cookies to enhance your experience on the site
CodeWorlds

Currying and Partial Application

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.

What is Currying?

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;

Practical Use of Currying

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, ... }

Universal Curry Function

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 result

Partial Application

Partial 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!'

Currying vs Partial Application

Although both techniques create specialized functions, they differ in how they work:

  • Currying: always creates a chain of single-argument functions
  • Partial application: "freezes" any number of arguments at once
Go to CodeWorlds