3 ms·
Indeed but we can get around closures by using first class operators like bind. This implementation I wrote is much more elegant and performant than the O(n) s
by goldenkey 5y ago
Indeed but we can get around closures by using first class operators like bind.
This implementation I wrote is much more elegant and performant than the O(n) stack frame crud in that article:
const curry = fn => {
const curried = (...a) => a.length >= fn.length ? fn(...a) : curried.bind(undefined, ...a)
return curried
}
In fact, I can't think of any implementation in JS that would be more performant, using any paradigm, functional, object oriented, or imperative.
Try to best it if you can. I'm curious to see what HN can come up with ;-)
- masklinn 5y agoThat's not currying, that's partial application (except bad because it forces the evaluation of the function if all parameters are filled, which is often undesirable as javascript is an eagerly evaluated language with side-effects).
- goldenkey 5y agoThe implementation in the article is technically partial application. I was just reproducing it. But you are correct. Currying is a subset of partial application. You can do currying through partial application. The function is eventually called when the correct number of parameters has finally been supplied. Currying lets you call a function, splitting it in multiple calls, providing one argument per-call. Partial Application lets you call a function, splitting it in multiple calls, providing multiple arguments per-call. The only difference is that currying would ignore any additional parameters in a flexible language like JS. It's easy to make this implementation only consider the first parameter. See below. const curry = fn => { const curried = (a,x) => a.length + 1 >= fn.length ? fn(...a, x) : curried.bind(undefined, [...a, x]) return curried.bind(undefined, []) } I still don't understand your point about eager execution. The function is supposed to be executed when enough parameters have been passed. I suppose you mean that JS does not check if the call arguments length equals the function signature, so strict currying is safer to avoid errors? If so, that makes sense. I tend to consider that caveat of JS a feature though, which is why partial application is what I'd use to accomplish currying. It subsumes it and provides more flexibility by allowing multiple arguments to be curried in a single call.
- masklinn 5y ago> I still don't understand your point about eager execution. The function is supposed to be executed when enough parameters have been passed. Which makes perfect sense for languages which are mostly side-effect free and in which functions with no parameters do not make sense. That's not the case of Javascript, partially applying all the parameters but not wanting to actually run the function is a useful thing to desire, and not doing that makes partial application incoherent, especially as e.g. default parameters get involved. That is why currying is a bad fit for imperative languages, but partial application is a useful tool still.
- goldenkey 5y agoYou're not making any sense to me. Neither currying nor partial application delay running of the function once all parameters have been passed. There is no eager execution happening in the implementation I provided. I believe you misunderstand when the original function is supposed to be executed. sum = curry((a,b,c) => a+b+c) sum(1)(2)(3) equals 6 under currying. sum(1,2)(3) equals 6 under partial application. Perhaps you are thinking of calling the function with no arguments as a signal to execute the final calculation. That is NOT how currying normally works. I believe you think currying works like this: sum(1)(2)(3)() = 6 This is NOT standard currying. There is never a need to give an empty call to signal execution in standard currying as CS and Mathematics define it. The implementation I provided is defacto currying by the book. Perhaps you can provide an example if we are misunderstanding eachother.
- masklinn 5y ago> Neither currying nor partial application delay running of the function once all parameters have been passed. Currying does not, partial application can (and should). > sum = curry((a,b,c) => a+b+c) > sum(1)(2)(3) equals 6 under currying. > sum(1,2)(3) equals 6 under partial application. Sure. Now what happens to `sum(1, 2, 3)`? Or if the function is `(a, b, c=1) => a + b + c`? > Perhaps you are thinking of calling the function with no arguments as a signal to execute the final calculation. That is NOT how currying normally works. Which is the point. It's not how currying works. It is, however, how a "regular" version of partial application works e.g. Javascript's Function#bind or Python's `functools.partial`. > I believe you are confused. Nope. > I believe you think currying works like this: No. > The implementation I provided is defacto currying by the book. "Currying by the book" does is defined for strict arities and can not generate variable-arity functions.