Update currying function to use bind instead of wrapper pass

I think this change will help us to dodge concatenating new arguments with lexical ones and make the currying function much simpler.
This commit is contained in:
Lakshya Thakur 2021-01-09 17:00:00 +05:30 committed by GitHub
parent 32518b7e76
commit 5965ae79a8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -121,11 +121,7 @@ function curry(func) {
return function curried(...args) { return function curried(...args) {
if (args.length >= func.length) { if (args.length >= func.length) {
return func.apply(this, args); return func.apply(this, args);
} else { } else return curried.bind(this, ...args);
return function(...args2) {
return curried.apply(this, args.concat(args2));
}
}
}; };
} }
@ -154,26 +150,22 @@ The result of `curry(func)` call is the wrapper `curried` that looks like this:
function curried(...args) { function curried(...args) {
if (args.length >= func.length) { // (1) if (args.length >= func.length) { // (1)
return func.apply(this, args); return func.apply(this, args);
} else { } else return curried.bind(this, ...args);
return function pass(...args2) { // (2)
return curried.apply(this, args.concat(args2));
}
}
}; };
``` ```
When we run it, there are two `if` execution branches: When we run it, there are two `if` execution branches:
1. Call now: if passed `args` count is the same as the original function has in its definition (`func.length`) or longer, then just pass the call to it. 1. Call now: if passed `args` count is the same as the original function has in its definition (`func.length`) or longer, then just pass the call to it.
2. Get a partial: otherwise, `func` is not called yet. Instead, another wrapper `pass` is returned, that will re-apply `curried` providing previous arguments together with the new ones. Then on a new call, again, we'll get either a new partial (if not enough arguments) or, finally, the result. 2. Get a partial: otherwise, `func` is not called yet. Instead, a new bounded function using curried is returned, that takes the `...args` i.e. the current arguments as pre-specified. Then on a new call, again, we'll get either a new partial (if not enough arguments) or, finally, the result.
For instance, let's see what happens in the case of `sum(a, b, c)`. Three arguments, so `sum.length = 3`. For instance, let's see what happens in the case of `sum(a, b, c)`. Three arguments, so `sum.length = 3`.
For the call `curried(1)(2)(3)`: For the call `curried(1)(2)(3)`:
1. The first call `curried(1)` remembers `1` in its Lexical Environment, and returns a wrapper `pass`. 1. The first call `curried(1)` returns a new bounded `curried` with `1` as pre-specified argument.
2. The wrapper `pass` is called with `(2)`: it takes previous args (`1`), concatenates them with what it got `(2)` and calls `curried(1, 2)` with them together. As the argument count is still less than 3, `curry` returns `pass`. 2. The bounded `curried` is called with `(2)`: it takes previous args (`1`) due to bind, and new leading argument `(2)` and calls `curried(2)`. As the argument count is still less than 3, `curry` returns new bounded `curried` with (`1`, `2`) as pre-specified arguments.
3. The wrapper `pass` is called again with `(3)`, for the next call `pass(3)` takes previous args (`1`, `2`) and adds `3` to them, making the call `curried(1, 2, 3)` -- there are `3` arguments at last, they are given to the original function. 3. The bounded `curried` is called again with `(3)`, for the next call `curried(3)` takes previous args (`1`, `2`) and new leading argument `3`, making the call `curried(3)` -- there are `3` arguments at last, they are given to the original function.
If that's still not obvious, just trace the calls sequence in your mind or on paper. If that's still not obvious, just trace the calls sequence in your mind or on paper.