Search code examples
javascriptclosurescurrying

Having trouble understanding JavaScript currying implementation


Driver code

const join = (a, b, c) => {
    return `${a}_${b}_${c}`
}

const curriedJoin = curry(join)
curriedJoin(1)(2)(3)

Implementation

function curry(fn) {
    return function curried(...args) {
        if (args.length >= fn.length) return fn(...args)

        return function(...nextArgs) {
            return curried(...args, ...nextArgs)
        }
    }
}

I'm having trouble understanding this implementation of a curried function in JavaScript, specifically the recursive case.

I think that curried is returning an anonymous function that expects some arguments, and wraps another call of curried passing the last call and current call's combined arguments.

I don't understand how on the next call of curry, as in curriedJoin(2), the execution jumps to the wrapped function's return curried(...args, ...nextArgs) line. Why does the execution not run through the function line by line rather than "remembering" to call this anonymous function?

I have a vague understanding of closures and I understand that the execution context is alive after the function returns - I think this is where my confusion lies.


Solution

  • I don't understand how on the next call of curry, as in curriedJoin(2), the execution jumps to the wrapped function's return curried(...args, ...nextArgs) line. Why does the execution not run through the function line by line rather than "remembering" to call this anonymous function?

    That is because only the first call, i.e. curriedJoin(1), calls the curried function directly. Calls after this, i.e. curriedJoin(1)(2), call the function returned by the curried function.

    As far as closures are concerned, closure is just a function saving a reference to the scope in which it is created. Example, function created in global scope has a reference to the global scope; similarly, curried function has a reference to the local scope of curry function.

    It is this reference to the outer scope that allows a function to look for declarations in the outer scope that are not defined in its local scope.