1. Explain currying and partial application. How are they different? Provide practical examples.
Currying and partial application are both function transformation techniques, but they work differently. Currying: Transforms a multi-argument function into a chain of single-argument functions. Each function takes exactly one argument and returns another function. The transformation is complete, all arguments become separate functions. Enables calling with one argument at a time. Partial application: Pre-fills some arguments of a function and returns a new function expecting remaining arguments. Does not require single-argument functions. The new function can take multiple arguments. More flexible than currying. Key differences: Currying always returns unary functions, one argument each. Partial application returns functions that can take multiple arguments. Currying is a complete transformation. Partial application is selective fixing. Currying enables easier composition. Partial application is more practical for everyday use. When to use currying: Function composition pipelines. When you want uniform interface of single-argument functions. Mathematical or functional programming patterns. Creating reusable function factories. When to use partial application: Creating specialized versions of generic functions. Event handlers with pre-set context. Configuration functions. Creating utility functions from general ones. Practical benefits: Code reusability by creating specialized functions. Better readability with descriptive function names. Delayed execution with pre-configured parameters. Easier testing of complex functions. Both techniques are fundamental to functional programming and common in interviews testing advanced JavaScript knowledge.
// CURRYING
// Regular function
function add(a, b, c) {
return a + b + c;
}
console.log(add(1, 2, 3)); // 6
// Curried version - each takes one argument
function addCurried(a) {
return function(b) {
return function(c) {
return a + b + c;
};
};
}
console.log(addCurried(1)(2)(3)); // 6
// Arrow function currying
const addArrow = a => b => c => a + b + c;
// Creating specialized functions
const add1 = addArrow(1);
const add1And2 = add1(2);
console.log(add1And2(3)); // 6
console.log(add1And2(5)); // 8
// Generic curry function
function curry(fn) {
return function curried(...args) {
if (args.length >= fn.length) {
return fn.apply(this, args);
}
return function(...nextArgs) {
return curried.apply(this, args.concat(nextArgs));
};
};
}
const curriedAdd = curry(add);
// All these work:
console.log(curriedAdd(1)(2)(3)); // 6
console.log(curriedAdd(1, 2)(3)); // 6
console.log(curriedAdd(1)(2, 3)); // 6
console.log(curriedAdd(1, 2, 3)); // 6
// PARTIAL APPLICATION
function multiply(a, b, c) {
return a * b * c;
}
// Using bind for partial application
const multiplyBy2 = multiply.bind(null, 2);
console.log(multiplyBy2(3, 4)); // 24 (2 * 3 * 4)
// Custom partial function
function partial(fn, ...fixedArgs) {
return function(...remainingArgs) {
return fn(...fixedArgs, ...remainingArgs);
};
}
const double = partial(multiply, 2, 1);
console.log(double(5)); // 10 (2 * 1 * 5)
// KEY DIFFERENCE DEMONSTRATION
// Currying - MUST call with one arg at a time (or use curry helper)
const curriedMultiply = a => b => c => a * b * c;
// curriedMultiply(2, 3, 4); // Error! Need ()()() syntax
console.log(curriedMultiply(2)(3)(4)); // 24
// Partial - can call with multiple args
const partialMultiply = partial(multiply, 2);
console.log(partialMultiply(3, 4)); // 24 (works!)
// PRACTICAL EXAMPLES
// Example 1: Logging system with currying
const log = level => timestamp => message => {
console.log(`[${level}] ${timestamp}: ${message}`);
};
const errorLog = log('ERROR');
const errorLogToday = errorLog('2024-01-15');
errorLogToday('Database connection failed');
errorLogToday('Timeout error');
// Example 2: API calls with partial application
function apiCall(method, url, headers, body) {
console.log(`${method} ${url}`, { headers, body });
}
const postRequest = partial(apiCall, 'POST');
const postToUsers = partial(postRequest, '/api/users');
postToUsers({ auth: 'token' }, { name: 'John' });
// Example 3: Discount calculator with currying
const discount = percent => price => {
return price - (price * percent / 100);
};
const tenPercentOff = discount(10);
const twentyPercentOff = discount(20);
console.log(tenPercentOff(100)); // 90
console.log(twentyPercentOff(100)); // 80
// Example 4: Validation with partial application
function validate(type, message, value) {
if (type === 'email') {
return value.includes('@') || message;
}
if (type === 'minLength') {
return value.length >= 8 || message;
}
}
const validateEmail = partial(validate, 'email', 'Invalid email');
const validatePassword = partial(validate, 'minLength', 'Too short');
console.log(validateEmail('test@email.com')); // true
console.log(validatePassword('12345')); // 'Too short'
// Example 5: Function composition with currying
const add5 = x => x + 5;
const multiply2 = x => x * 2;
const subtract3 = x => x - 3;
// Compose works better with curried functions
const compose = (...fns) => x => fns.reduceRight((acc, fn) => fn(acc), x);
const calculate = compose(subtract3, multiply2, add5);
console.log(calculate(10)); // ((10 + 5) * 2) - 3 = 27
// WHEN TO USE WHAT
// Use currying for:
// - Function composition
// - Creating chains of specialized functions
// - Functional programming patterns
// Use partial application for:
// - Creating utility functions
// - Event handlers with context
// - Configuration functions
// - Everyday practical coding