Some tips when using currying in Javascript

Sometimes, you may find currying hard to understand and wonder about its application. In this post, I will list some tips which may helpful for you
Huy Ngo
Apr 21, 2020

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