-
Notifications
You must be signed in to change notification settings - Fork 69
Currying #175
Comments
I would expect the following: f (a, b) = a + b
g a b = a + b to become: var f = function(a, b) {
return a + b;
};
var g = function(a) {
return function (b) {
return a + b;
};
}; This would allow you to still use the tupled functions when you need to for compatibility. We can then have a functions to convert between curryable and tupled functions: curry2 f a b = f (a, b)
curry3 f a b c = f (a, b, c)
etc.
uncurry2 f (a, b) = f a b
etc. ----> var curry2 = function (f) {
return function (a) {
return function (b) {
return f(a,b);
};
};
};
etc.
var uncurry2 = function (f) {
return function (a, b) {
return f(a)(b);
};
};
etc. |
@pufuwozu sorry, I don't really understand that last example. |
@joneshf sorry, that made no sense. Just updated it with what I meant. |
By this logic, lambdas could be lifted, too. f a b = \x -> a + x
g b = f 12 b 12 ---> function f (a, b, x) {
return a + x;
};
function g (b) {
return f(12, b, 12);
}; But I'm not sure that is a good idea. |
@zeckalpha I kind of like the idea of a curry function, though I think we can generalize it. var curry = function(f) {
function currier(curriedArgs) {
// We need at least as many args as the function takes.
if (curriedArgs.length >= f.length) {
// Pass them all into the function.
// We could only pass as many as needed,
// but some goof could be using unnamed args like this is.
return f.apply(this, curriedArgs);
} else {
// Not enough args, let's curry it with what ever else comes down the pipe later
return function() {
var newArgs = curriedArgs.concat(Array.prototype.slice.call(arguments));
return currier(newArgs);
};
}
}
// Grab only the arguments.
return currier(Array.prototype.slice.call(arguments, 1));
}; Something like that, except with more clarity. So we should be able to do things like: let f x y = x + y
let g = f 37
console.log (g 3) and have it compile to var f = function(x, y) {
return x + y;
};
var g = curry(f, 37);
console.log(g(3));
var curry = function(f) {
function currier(curriedArgs) {
if (curriedArgs.length >= f.length)
return f.apply(this, curriedArgs);
return function() {
return currier(curriedArgs.concat(Array.prototype.slice.call(arguments)));
};
}
return currier(Array.prototype.slice.call(arguments, 1));
}; |
Perhaps a better name for this function would be |
Ah, thanks for the correction. So I guess the question is, can a generalized curry function be written that is readable? Or do we cut our losses and just say partial application is good enough? How would a plain js developer do it? Would they write the partial, or try for true currying? |
I'm not really one, so I'm not sure exactly, but I don't think they would do it! The only use cases I can think of for partial application use an object passed in instead, e.g. in Underscore, the |
+1 for the original "caller side" proposal that generates the following for
If exported Roy functions are curried, calling Roy libraries from vanilla JS will entail This is an especially important consideration for people wanting to incorporate Roy into existing codebases incrementally: rewrite one piece of legacy code in Roy, then another, then another... If Roy functions require a different calling style, then implementing drop-in rewrites of individual JS functions or modules is off the table. |
Can't compatibility be managed using tupled arguments?
|
@zeckalpha Scala does something like that: def f(a: (Int, Boolean), b: String) = ???
def g(a: (Int, Boolean))(b: String) = ??? (I used a tuple as an argument type just to show that you can still have that) |
Tupled arguments are one approach to compatibility, but it means Roy authors have to consciously code with compatibility in mind. As a Roy author I'd rather just code naturally and have the compiler take care of generating JS-friendly code. |
I like the ability to have both tupled and curried functions in ML and Haskell. I found the curried notation and tupled semantics confusing coming to Roy. |
@zeckalpha you can still have tupled arguments but they're not going to compile to a multiple argument JavaScript call. It'd have to be a tuple object. (Just like Haskell) |
Yeah, that's how I would expect it to work from the JS side. |
A pollyfilled |
That works too. As an aside I like the idea of saying "Roy targets ES5, so include these polyfill libraries if you need to target lower than that", or include something along the lines of a --polyfills compiler flag to allow you to opt into (or maybe opt out of) having polyfills included in the generated source. The most important part to me is getting the best of both worlds: automatic currying when I'm writing Roy, and standard invocation style when I'm calling Roy functions from JS. |
CoffeeScript used to do this when you used its "fat arrow" (now it's smart enough to just alias var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; I think that's a good default. Roy should probably have an ES5 mode to allow you to not have that and just rely on a builtin |
Sure, that's definitely a minor optimization. |
👍 |
@joneshf, I'm assuming by "plain JS developer" you mean a developer who writes plain JS in a functional style. |
I haven't looked at it too indepth, but underscore-contrib seems to actually do this: https://github.com/documentcloud/underscore-contrib/blob/master/underscore.function.arity.js#L127-L176 Based on https://github.com/eborden/js-curry But yeah, it seems like it's not the norm. |
Functions definitions are compiled to an un-curried form, and function calls act accordingly. Also, not calling a function with enough arguments now generates a wrapping function. let f x y = x + y let g = f 1 console.log (g 2) // The above Roy compiles into this var f = function (x, y) { return x + y; } var g = function (_1) { return f(1, _1); } console.log(g(2)); Resolves #175
Very nice @nickmeharry! Can't wait for constraints to land. |
One of the goals for Roy is to ensure the JavaScript output should easily be used from plain JavaScript (and hopefully other languages).
Here we want
f
to be a curried function:The problem with currying each function definition is that it doesn't compile to great JavaScript:
But maybe we could implement currying on the caller side?
Yes, the JavaScript it not great - but only if you use the currying. Plain JavaScript clients don't change.
The text was updated successfully, but these errors were encountered: