Spice up your Javascript with some powerful curry! (Functional Programming and Currying)
Tags: javascript, tutorial, webdev, react
Published: 2021-08-03 19:07:55

Functional programming and currying are topics that have some of us staring at the wall and saying something like "there is no spoon", whilst sadly shaking our heads.  Yet we know that there is a powerful tool sitting there, so we struggle on in a bid for mastery of the dark arts.

I started life as a C/C++ programmer and over the years I've made money in a whole bunch of languages, but functional programming proved to be a very different path.  I've come some way down this track, so I thought I'd share my understanding and one of the utilities I've made along the way.

## Basics

Let's start with the basics.

If you have a function:

```js
const calculate = (a, b, c) => (a * b) / c 
```

You could rewrite it as:

```js
const calculate = a => b => c => (a * b) / c
```

You'd call the first one like this:

```js
   console.log(calculate(100, 20, 3))
```

And you'd call the second one like this:

```js
   console.log(calculate(100)(20)(3))
```

The second implementation is a function, which creates a function, which creates a function to calculate the answer (this is moving from The Matrix into Inception huh?)

We converted the original using Javascript arrow functions and basically replacing `a,` with `a =>`.  The first function returns takes the parameter `a` and returns a function for the parameter `b`. Thanks to closures the final function has access to all of the previous parameters and so can complete its work.  

The benefit of this is code reuse.  Until the last function we are basically running a factory to create functions that have the already supplied parameters baked in.

```js
  const calculateTheAnswer = calculate(100)(20)
  for(let i = 1; i < 1000; i++) {
     console.log(calculateTheAnswer(i))
  }
```

Now in this case you might be saying "oh nice, seems ok, can't see the point though".  The strength comes when you start making more complicated things by passing functions around as parameters and "composing" solutions out of multiple functions.  Lets take a look.

## Currying

For the sake of this article I want an example that is simple, yet not only "multiplying two numbers together".  So I've come up with one that involves multiplying and taking away ;) Seriously though, I hope the that it proves to give a practical perspective.

Ok, so imagine we are building a website for a manufacturing company and we've been tasked with displaying the weights of the company's "UberStorage" containers when made in a variety of sizes and materials. 

Some smart bloke has provided us with access to a library function to calculate the weight of a unit.  

```js
function weightOfHollowBox(
    edgeThickness,
    heightInM,
    widthInM,
    depthInM,
    densityInCm3
) {
    return (
        heightInM * widthInM * depthInM * (densityInCm3 * 1000) -
        (heightInM - edgeThickness * 2) *
            (widthInM - edgeThickness * 2) *
            (depthInM - edgeThickness * 2) *
            (densityInCm3 * 1000)
    )
}
```
(See multiplying and taking away).  We don't want to mess with this as it isn't our code and might change, but we can rely on the "contract" of the parameters being passed.

Our website is going to need to display lots of different output like this:

{% codesandbox br1yq %}

So we are going to have to iterate over dimensions and materials and produce some output.

We want to write the minimum code possible, so we think of functional programming and curry!

Firstly we could make up a wrapper to that function:

```js
const getHollowBoxWeight = (edgeThickness) => (heightInM) => (widthInM) => (
    depthInM
) => (densityInCm3) =>
    weightOfHollowBox(
        edgeThickness,
        heightInM,
        widthInM,
        depthInM,
        densityInCm3
    )
```

But immediately we start to see some problems, we have to call the functions in the right order, and given our problem we need to think hard to see if we can make up a perfect order that maximises reuse. Should we put density first? That's a property of the material.  edgeThickness is standard for most of our products so we could put that first. Etc etc.  What about the last parameter, we probably want that to be the thing we iterate over, but we are iterating both material and dimensions. Hmmmm.

You might be fine writing a few versions of the wrapper function, you might be fine throwing the towel in saying "I'll just call weightOfHollowBox" but there is another option.  Use a *curry* maker to convert the `weightOfHollowBox` to a curried function.

### Simple curry, not too many ingredients

Ok so a simple curry function would take `weightOfHollowBox` as a parameter and return a function that can be called with a number of the arguments.  If we have completed all of them, calculate the weight, otherwise return a function that needs the remaining parameters.  Such a wrapper would look a bit like this:

```js
const currySimple = (fn, ...provided) => {
    // fn.length is the number of parameters before
    // the first one with a default value
    const length = fn.length
    // Return a function that takes parameters
    return (...params) => {
        // Combine any parameters we had before with the
        // new ones
        const all = [...provided, ...params]
        
        // If we have enough parameters, call the fn
        // otherwise return a new function that knows
        // about the already passed params
        if (all.length >= length) {
            return fn(...all)
        } else {
            return currySimple(fn, ...all)
        }
    }
}
```

If we call this on weightOfHollowBox we end up with a function that is a little more flexible than the hand written one:

```js
   const getWeightOfBox = currySimple(weightOfHollowBox)

   // All of these combinations work
   console.log(getWeightOfBox(0.1)(10)(10)(3)(.124))
   console.log(getWeightOfBox(0.1, 10, 10)(3)(.124))
```
We can pass all of the parameters or any subset and it works in those cases.  This **does not** solve our parameter ordering issue.  We would dearly love a version of this that allowed us to miss out interim parameters and have a function for just those.

e.g.

```js
   const getWeightOfBox = curry(weightOfHollowBox)
   const varyByWidth = getWeightOfBox(0.1, 10, MISSING, 3, .124)
   console.log(varyByWidth(4))
```

### Jalfrezi

> **Warning** there follows some much more advanced code to create this new `curry` function - you don't need to understand it if you don't want to.  You could use this implementation or one of the many others out there without needing to get the inner workings.  If you want to see how this is done read on, otherwise skip to the next section.

Ok lets cook up some proper curry.  First we need something that uniquely identifies a missing parameter.

```js
const MISSING = Symbol("Missing")
```

With that in our toolbox, we can go ahead and write our new curry function.

```js
const curry = (
    fn,
    missingParameters = Array.from({ length: fn.length }, (_, i) => i),
    parameters = []
) => {
    return (...params) => {
        // Keeps a track of the values we haven't supplied yet
        const missing = [...missingParameters]
        // Keeps a track of the values we have supplied
        const values = [...parameters]

        // Loop through the new parameters
        let scan = 0
        for (let parameter of params) {
            // If it is missing move on
            if (parameter === MISSING) {
                scan++
                continue
            }
            // Update the value and the missing list
            values[missing[scan] ?? values.length] = parameter
            missing.splice(scan, 1)
        }
        // Call the function when we have enough params
        if (missing.length <= 0) {
            return fn(...values)
        } else {
            // Curry again? Yes please
            return curry(fn, missing, values)
        }
    }
}
```

Right, let's start with those parameters. The `fn` is the function to be curried, the next two we use when recursing through in the case that we need to make another intermediate function rather than call `fn`.  `missingParameters` defaults to the numbers 0..n where `n` is the number of parameters required by `fn` - 1.  In other words, when we first call it, it is the indices of all of the parameters required for `fn`. The next parameter is an empty array that we will populate and pass down should we need to.

The function we return takes any number of parameters.  We take a copy of the missing indices and the existing parameters and then we iterate over the new parameters.  If the parameter value is `MISSING` we move on to the next missing index.  When it isn't `MISSING` we populate the correct index in the values array (which we allow to take more parameters than the function, as that's how you deal with any that might have been defaulted).  Having populated the array we remove the missing index.

Once that's all done, if the missing list is empty then we call the function, passing it the values, otherwise we recurse.

> Note: we never set the length of the array, Javascript arrays automatically set their length to the maximum value if you write to an index in them.

That's it, this function allows us to create a range of templates.

## Example Web Site

{% codesandbox br1yq %}

Now we have a way of wrapping `weightOfHollowBox` we can start to put together the elements of our web page.

Firstly lets code up the thing that shows the weight of an item and its material.  We can see that the inner item is something based on iterating over the material.  We have this definition of materials:

```js
const materials = [
    { name: "Aluminium", density: 2.71 },
    { name: "Steel", density: 7.7 },
    { name: "Oak", density: 0.73 }
]
```

So we write a curried function to render the item that takes a way to calculate the weight (a function we will create from our curried `weightOfHollowBox`) and a material:

```js
const material = (weightInKg) => (material) => (
    <ListItem key={material.name}>
        <ListItemText
            primary={material.name}
            secondary={
                <span>
                    {(weightInKg(material.density) / 1000).toFixed(1)} tons
                </span>
            }
        />
    </ListItem>
)
```

This will display any material so long as we can give it a function to calculate the weight that requires the density.

Let me show you a simple way this could now be used:

```js
function Simple() {
    const weightInKg = curriedWeight(0.05, 10, 3, 3)
    return (
        <List className="App">
            {materials.map(material(weightInKg))}
        </List>
    )
}
```

We create a weight calculator looking for `density` and then we call our material function, passing that, which returns a function that needs a `material`, this will be passed by the `materials.map()`.

We are going to do something fancier for the site though.  

### A block for all materials

We want to output a list of materials so let's write a function for that.

```js

const materialBlock = (header) => (weightCalculator) => (
    materials
) => (dimension) => (
    <Fragment key={dimension}>
        {header(dimension)}
        {materials.map(material(weightCalculator(dimension)))}
    </Fragment>
)
```

This curried function allows us to supply something that will write a header, then given a weight calculator, a list of materials and a dimension it will output all of the materials for that group.

That's a bit trickier, let's see how we might use that in an isolated way:

```js
const ShowByHeight = () => {
    const heights = [2, 3, 5, 10]
    const weightCalculator = curriedWeight(0.05, MISSING, 5, 3)
    const outputter = materialBlock((height) => (
        <ListSubheader>5 m wide x {height} m tall</ListSubheader>
    ))(weightCalculator)(materials)
    return <List className="App">{heights.map(outputter)}</List>
}
```

Here we have a React component that knows the standard heights of our units.  It creates a weight calculator that still requires `height` and `density` and then provides `materialBlock` with a header to put over it.

For the site we can get better code reuse though!

```js
const ShowBy = (weightCalculator) => (header) => (values) => (
    <List className="App">
        {values.map(
            materialBlock(header)(weightCalculator)(materials)
        )}
    </List>
)
```

We create a reusable ShowBy function, which we can then use to create versions for our standard widths and heights.

```js
const widths = [1, 4, 7, 10]
const heights = [2, 3, 5, 10]

const ByWidth = () =>
    ShowBy(curriedWeight(0.05, 10, MISSING, 3))((width) => (
        <ListSubheader>10 m tall x {width} m wide</ListSubheader>
    ))(widths)

const ByHeight = () =>
    ShowBy(curriedWeight(0.05, MISSING, 5, 3))((height) => (
        <ListSubheader>5 m wide x {height} m tall</ListSubheader>
    ))(heights)
```

### Pulling it together

Our final function is used to put the parts together:

```js

const Advanced = () => (
    <Box>
        <Box mb={2}>
            <Card>
                <CardHeader title="By Width" />
                <CardContent>
                    <ByWidth />
                </CardContent>
            </Card>
        </Box>
        <Box mb={2}>
            <Card>
                <CardHeader title="By Height" />
                <CardContent>
                    <ByHeight />
                </CardContent>
            </Card>
        </Box>
    </Box>
)
```

Here's the whole thing:

{% codesandbox br1yq view=editor %}

## Conclusion

I hope this has been an interesting look at currying in Javascript.  The area of functional programming is very deep and we've only scratched the surface, but there exist here some techniques that are practical to use in many scenarios.

Thanks for reading!

(All code MIT licensed)

{% codesandbox n4dpo initialpath=/bb-gNbDtOv4P60wDfIv4J/embed %}