Lodash flowRight: Mathematical Function Composition
Lodash's _.flowRight method provides a direct
implementation of mathematical function composition for JavaScript
applications. By accepting a series of functions and invoking them from
right to left, _.flowRight replicates the standard
algebraic notation where the output of one operation automatically feeds
into the next. This article breaks down the mechanics of mathematical
composition, illustrates how _.flowRight mirrors this
behavior, and explores how it simplifies nested operations in functional
programming.
The Principle of Mathematical Composition
In mathematics, function composition combines two or more functions to produce a new function. Given two functions, \(f\) and \(g\), their composition is formally written as:
\[(f \circ g)(x) = f(g(x))\]
In this notation, evaluation occurs from the inside out, or right to left. The inner function \(g(x)\) executes first using the original input \(x\). The resulting value is then passed directly as the argument to the outer function \(f\).
If extended to three functions—\((f \circ g \circ h)(x)\)—the execution order remains strictly right-to-left: \(h(x)\) executes first, \(g\) receives the result of \(h\), and finally \(f\) receives the result of \(g\).
How Lodash _.flowRight
Works
The Lodash library provides two composition utilities:
_.flow and _.flowRight. While
_.flow executes functions from left to right (similar to a
Unix pipe), _.flowRight mirrors traditional mathematical
composition by executing functions from right to left.
When invoking _.flowRight(f, g, h), Lodash returns a new
composed function. When this composed function receives an argument,
Lodash runs h, passes its return value to g,
and finally passes that return value to f.
const _ = require('lodash');
const addTwo = (n) => n + 2;
const multiplyByThree = (n) => n * 3;
const square = (n) => n * n;
// Traditional nested execution: square(multiplyByThree(addTwo(4)))
// Mathematical representation: (square ∘ multiplyByThree ∘ addTwo)(4)
const compute = _.flowRight(square, multiplyByThree, addTwo);
const result = compute(4);
// Execution steps:
// 1. addTwo(4) => 6
// 2. multiplyByThree(6) => 18
// 3. square(18) => 324
console.log(result); // Output: 324Argument Arity and Execution Rules
To accurately represent functional composition while maintaining
JavaScript compatibility, _.flowRight follows specific
rules regarding arguments:
- Initial Invocation Arity: The rightmost (first executed) function can accept multiple arguments (an arity of greater than or equal to 1). This allows the composed pipeline to begin with complex operations such as summing two numbers or merging objects.
- Subsequent Invocations: Every preceding function in the chain receives only a single argument—the return value of the function executed immediately before it.
const formatCurrency = (amount) => `$${amount.toFixed(2)}`;
const applyDiscount = (total) => total * 0.9;
const calculateSubtotal = (price, quantity) => price * quantity;
// calculateSubtotal is the rightmost function and accepts two arguments
const getFinalPrice = _.flowRight(
formatCurrency,
applyDiscount,
calculateSubtotal
);
console.log(getFinalPrice(100, 2)); // Output: "$180.00"Advantages Over Deep Nesting
In standard JavaScript, replicating \((f \circ g \circ h)(x)\) without a composition utility requires manual nesting:
const result = f(g(h(x)));As the number of operations increases, deeply nested calls become difficult to read, maintain, and debug. They introduce visual noise via excessive parentheses and force developers to track data flow inside-out across a single line of code.
Using _.flowRight flattens this structure into a
declarative definition. It creates reusable, point-free functions that
describe what transformations occur rather than imperatively
managing intermediate variables or nested syntax.