Currying Inside JavaScript
Tags: javascript, webdev
Published: 2019-09-10 03:25:58

Find me on [medium](https://medium.com/@jsmanifest)

**Currying** is an advanced technique when working with functions and it's used in multiple programming languages.

When you break down a function that takes _multiple arguments_ into a _series of nesting functions_, you have a _curry_. Each nesting function will be expecting to have the next argument(s) to the function.

The curry function will always be returning a _new function_ each time until all of the arguments were received for each invocation. These arguments are able to live throughout the lifetime of the currying through _closure_ and will all be used to execute the final function.

A very basic example can look something like this:

```js
function combineWords(word) {
  return function(anotherWord) {
    return function(andAnotherWord) {
      return `${word} ${anotherWord} ${andAnotherWord}`
    }
  }
}
```

To use this, you can invoke the function a number of times until it reaches the last function:

```js
const result = combineWords('hello,')('good')('morning')
console.log(result)

// result: 'hello, good morning'
```

So what's happening is that `combineWords` is a curried function (obviously) and waits for a word to be given before it executes the next function in the series. You can bind `'wow!'` to `combineWords` to a variable and re-use it to create other greetings that start with `'wow!'`:

```js
let greet = combineWords('wow!')
greet = greet('nice')

console.log(greet('jacket'))
console.log(greet('shoes'))
console.log(greet('eyes'))
console.log(greet('socks'))
console.log(greet('hat'))
console.log(greet('glasses'))
console.log(greet('finger nails'))
console.log(greet('PS3'))
console.log(greet('pet'))

/*
result:
  "wow! nice jacket"
  "wow! nice shoes"
  "wow! nice eyes"
  "wow! nice socks"
  "wow! nice hat"
  "wow! nice glasses"
  "wow! nice finger nails"
  "wow! nice PS3"
  "wow! nice pet"
*/
```

If the concept is a little hard to understand, try reading it this way:

> The mother is expecting all 4 eggs (arguments) before cooking and her 4 children will each carry one to her, one at a time.

```js
function Egg() {...}

// the curry func
function prepareCooking(cook) {
  return function(egg1) {
    return function(egg2) {
      return function(egg3) {
        return function(egg4) {
          return cook(egg1, egg2, egg3, egg4)
        }
      }
    }
  }
}

const cook = function(...eggs) {
  api.turnOnStove()
  api.putEggsOnTop(...eggs)
  api.pourSalt()
  api.serve()
  console.log('served children')
  return 'served'
}

const start = prepareCooking(cook)

let collect = start(new Egg())
collect = collect(new Egg())
collect = collect(new Egg())
collect = collect(new Egg()) // this steps into the last function witih argument "egg4" which will invoke the callback passed to "prepareCooking"

// result:  console.log --> "served children"
// collect === 'served'
```

In order for the `cook` callback to be invoked, all of the 4 eggs needed to be passed in one after the other, each prefilling the next function awaiting for invocation.

If you were to stop at the third egg:

```js
let collect = start(new Egg())
collect = collect(new Egg())
collect = collect(new Egg())
```

Then since the last function expecting `egg4` has not been reached yet, the value of `collect` is that function:

```js
function prepareCooking(cook) {
  return function(egg1) {
    return function(egg2) {
      return function(egg3) {
        // HERE
        return function(egg4) {
          return cook(egg1, egg2, egg3, egg4)
        }
      }
    }
  }
}
```

To finish the curry, collect the last egg:

```js
let collect = start(new Egg())
collect = collect(new Egg())
collect = collect(new Egg())
collect = collect(new Egg())

// collect === 'served'
```

Now it's important to know that each nesting function has all access of the outer scope within the curry function. Knowing this, you can provide custom logic inbetween each nested function to tailor for specific situations. But it's best to leave a curry as a curry and nothing else.

A more advanced curry function can look as follows: (i'm going to provide an `ES5` version as well as an `ES6` because there are plenty of old tutorials that show ES5 syntax, which might be a little hard to read for newer JavaScript developers)

> ES5

```js
function curry(fn) {
  return function curried() {
    const args = Array.prototype.slice.call(arguments)
    const done = args.length >= fn.length
    if (done) {
      return fn.apply(this, args)
    } else {
      return function() {
        const args2 = Array.prototype.slice.call(arguments)
        return curried.apply(this, args.concat(args2))
      }
    }
  }
}
```

...is the same as:

> ES6

```js
const curry = (fn) => {
  return function curried(...args) {
    const done = args.length >= fn.length
    if (done) {
      return fn.apply(this, args)
    } else {
      return (...args2) => curried.apply(this, [...args, ...args2])
    }
  }
}
```

Let's explain this example more in detail:

When you call `curry(fn)` it will return the inner `curried` function that will wait for the next arguments upon invocation. Now when you call this inner function, it evaluates two conditions:

1. Did the caller pass in enough arguments to satisfy all the arguments of `fn`?
2. Or are there still arguments missing that `fn` needs?

If _number 1_ is the case, then we have all the arguments we need that `fn` declared and the curry will end by returning the invocation of `fn` and passing all the arguments received to it (basically invoking `fn` normally now)

However, if _number 2_ is the case, then the curry must continue going and we must somehow go back to the inner `curried` function so that we can continue to receive more arguments until it satisfies the arguments of `fn`. The code `return (...args2) => curried.apply(this, [...args, ...args2])` accumulates all the arguments exposed so far and uses them to continue the curry in this case.

There's one important rule:

> The function that is to be invoked before waiting for all the arguments to be collected must have a fixed number of arguments. This means that the function cannot have parameters spreaded (ex: `fn(...args)`)

ex:

```js
const curry = (fn) => {
  return function curried(...args) {
    const done = args.length >= fn.length
    if (done) {
      return fn.apply(this, args)
    } else {
      return (...args2) => curried.apply(this, [...args, ...args2])
    }
  }
}

// This is invalid because it uses ...args.  The curry does not understand where to stop
function func(...args) {
  //
}

const currying = curry(func)
```

## Conclusion

I think currying is an interesting technique because creating a curry involves composing other advanced techniques. There are closures involved, higher order functions, and recursion.

And that concludes the end of this post. I hope you found something valuable and look out for more in the future!

Find me on [medium](https://medium.com/@jsmanifest)