-
Notifications
You must be signed in to change notification settings - Fork 1.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Consider changing the curry implementation in Appendix A #588
Comments
Actually after looking at the function for while I decided an easier to read function curry (f) {
return isEmpty(f)
? f()
: (a, ...rest) => {
let curriedF = f.bind(null, a)
return isEmpty(rest)
? curry(curriedF)
: curry(curriedF)(...rest)
}
}
function isEmpty (l) {
return l.length === 0
} It also dawned on me that The same tests are of course still valid: const add = (x, y, z) => x + y + z
console.log(
curry(add)(1,2,3), // 6
curry(add)(1,2)(3), // 6
curry(add)(1)(2)(3), // 6
curry(add).length === 1, // true
curry(add)(1).length === 1, // true
curry(add)(1, 1).length === 1, // true
curry(add)(1)(1).length === 1, // true
// curry(add)(1,2,3,4), // TypeError
// curry(add)(1,2)(3,4), // TypeError
// curry(add)(1)(2)(3)(4), // TypeError
) |
a quick post to line up the 3
// curry :: ((a, b, ...) -> c) -> a -> b -> ... -> c
function curry(fn) {
const arity = fn.length;
return function $curry(...args) {
if (args.length < arity) {
return $curry.bind(null, ...args);
}
return fn(...args); // used to be: fn.call(null, ...args);
};
} // curry :: ((a, b, ...) -> c) -> a -> b -> ... -> c
function curry (f) {
if (f.length === 0) return f()
return (a, ...rest) => continuation(f.bind(null, a), rest)
}
function continuation(f, rest) {
return rest.length === 0
? curry(f)
: curry(f)(...rest)
} // curry :: ((a, b, ...) -> c) -> a -> b -> ... -> c
function curry (f) {
return isEmpty(f)
? f()
: (a, ...rest) => {
let curriedF = f.bind(null, a)
return isEmpty(rest)
? curry(curriedF)
: curry(curriedF)(...rest)
}
}
function isEmpty (l) {
return l.length === 0
} |
While I appreciate the simplicity of the
curry
implementation it does go against the general definition of Currying. I suggest an implementation that via a co-recursive accumulator function will apply each argument to a new function as in the wikipedia definition1.My main issue with the current implementation is that it silently ignore extra arguments which makes it rather difficult to trace extraneous arguments when function composition goes wrong. This also seem more inline with how Lisp is implemented (does not allow too many arguments).
Consider the following, with the current implementation:
Wrong usage of the
add
function, silently ignores data, leading to subtle and hard to find bugs.Now consider the following implementation and results:
It is not permitted to supply too many arguments to a curried function. Doing so will throw a
TypeError
.One limitation of my suggestion is that zero parameter functions will be invoked immediately which is never desirable.
Will fail with TypeError: curry(...) is not a function, with my implementation.
But currying zero parameter function also seem like a very edge case, adding an unnecessary layer of execution, that is better solved by having a
partial
for one-off function wrapping.partial
implementation.Lastly, my implementation does not let the user specify an
arity
which might be needed for variadic functions but neither does the one in Appendix A.My implementation is meant as a mean to start a discussion about popularise a curry function that actually turns a function into a sequence of functions that each take a single argument.
I apologise if this has been debated to death. It's not my intention to nag.
1: I have yet to find a formal definition of currying other than the wikipedia article.
The text was updated successfully, but these errors were encountered: