Some tips when using currying in Javascript
In this post, I assume that you already know Currying in Javascript. If you do not, I would recommend you to go through my previous post about currying prior to this post.
Curry in the right order
If you plan to use curried functions for function composition, remember to curry in the right order. The last primary input, which will be traveling along with the composition chain, should be put in the last function. While function composition is the strongest reason for currying, we should always think about it.
Let’s see an example:
const compose = (...fns) => fns.reduce((f, g) => (...args) => f(g(...args)));
// Right order
const map = fn => array => array.map(fn)
const double = map(x => x * 2)
const increase3 = map(x => x + 3)
const result = compose(double, increase3)([20]) // result: 46
// Wrong order, we cannot use it for composition
const map = array => fn => array.map(fn)
Flip a curried function
Then, what happens if you use a curried function from a third party library, and its order does not suitable for your composition? Flipping it may help you in this case.
const map = array => fn => array.map(fn)
const flippedMap = a => b => map(b)(a)
Add traceability to compose chain
You may argue about using compose
in your code. The main reason may come from its difficulties for debugging. The small snippet below can ease your process a bit:
const trace = label => x => {
console.log(label, x)
return x
}
const compose = (...fns) => fns.reduce((f, g) => (...args) => f(g(...args)));
const sum = a => b => a + b
const multiply = a => b => a * b
const addTransactionFee = sum(2)
const addTax = multiply(1.1)
const addMonthlyPromotion = multiply(0.8)
const paymentAmount = compose(
trace('addTransactionFee'),
addTransactionFee,
trace('addTax'),
addTax,
trace('addMonthlyPromotion'),
addMonthlyPromotion
)(100)
// Output:
// addMonthlyPromotion 80
// addTax 88
// addTransactionFee 90
Use pipe if you prefer reading in nature flow
As you can see in the sample above, execution order in compose
function is last in first serve
. This is a reverted way of our reading flow. If you prefer reading your code in a natural flow, I would suggest pipe
as an alternative.
const pipe = (...fns) => fns.reduceRight((f, g) => (...args) => f(g(...args)));
const trace = label => x => {
console.log(label, x)
return x
}
const sum = a => b => a + b
const multiply = a => b => a * b
const addTransactionFee = sum(2)
const addTax = multiply(1.1)
const addMonthlyPromotion = multiply(0.8)
const paymentAmount = pipe(
trace('addTransactionFee'),
addTransactionFee,
trace('addTax'),
addTax,
trace('addMonthlyPromotion'),
addMonthlyPromotion
)(100)
// Output:
// addTransactionFee 100
// addTax 102
// addMonthlyPromotion 112.2