My answers are almost always less intented for the person who's post I'm responding to, but more for a general audience. As others have already posted the part about "currying isn't partial application", and I mainly wanted to add a (hopefully) understanable example.
And I would say that taking the OP's post as if he doesn't exactly know, what currying means in comparison to partia application _is_ the more favourable interpretation of their post.
Same. I don't expect you're going to admit your mistake, but I'm pointing it out because the general audience is the sort of audience who is prone to this mistake (which I only know because I myself have been corrected for making this mistake).
> As others have already posted the part about "currying isn't partial application", and I mainly wanted to add a (hopefully) understanable example.
And in doing so, you're making the same mistake as those other posters, by assuming skybrian doesn't know what currying and partial application are. You're not doing your audience any favors by painting skybrian's post as if he said something wrong.
> And I would say that taking the OP's post as if he doesn't exactly know, what currying means in comparison to partia application _is_ the more favourable interpretation of their post.
How so?
It is easy to guess that the original comment meant that a manual re-write of a function:
foo(a,b) {...}
to a class (example in pseudo-C++, since C++ supports operator() overloading): class Foo {
Foo(a) {...}
operator()(b) {...}
}
is equivalent to currying the original function: // curried foo:
foo(a)(b)
// class-curried foo:
Foo(a)(b)
But instead of explaining or mentioning this, you both went into a rant about intentions and motivations, which completely bored me, and informed me of nothing, as a member of the general audience.Congratulations.
Your example is currying in a manual fashion, so two questions remain for me:
1. How would you tell the compiler to do it for you, as a function transformation?
2. How would you curry a function with 3+ arguments?
#include <iostream>
#include <functional>
template <typename Ret, typename First, typename ...Rest>
class Curry {
std::function<Ret(First, Rest...)> fn;
public:
Curry(std::function<Ret(First, Rest...)> fn): fn(fn) {};
Curry<Ret, Rest...> operator()(First arg) {
return Curry<Ret, Rest...>{[this, arg](Rest... args) {
return this->fn(arg, args...);
}};
}
};
template <typename Ret, typename Arg>
class Curry<Ret, Arg> {
std::function<Ret(Arg)> fn;
public:
Curry(std::function<Ret(Arg)> fn): fn(fn) {};
Ret operator()(Arg arg) {
return this->fn(arg);
}
};
int uncurried(int a, int b, int c) {
return a + 2*b + 3*c;
}
int main() {
auto curried = Curry<int, int, int, int>{uncurried};
std::cout << curried(1)(2)(3) << std::endl;
}Though if enough people share the confusion, I guess it becomes an alternative definition. Language evolves.
Also, in any case, a constructed object is nothing like a partially applied function, so the parallel drawn is not useful. It seems entirely reasonable in this case to assume that the poster does not have sufficient experience or knowledge.
A method is a function with convenient syntax. Some of its arguments come from the “this” parameter. If the object is immutable and the method has no side-effects, it’s a pure function.
Converting a function to an immutable class with a method is a mechanical refactoring that I sometimes do, when the class has an intuitive name. The code does the same thing.
I have to concur with grandparent and also with user https://news.ycombinator.com/user?id=whilenot-dev in a sibling subthread.
E.g. if we have (lambda (x y z) (+ x y z)) converted into the curried form (lambda (x) (lambda (y) (lambda (z) (+ x y z)))), then while we do not yet have partial application, we can then partially apply just by passing arguments.
That is to say, we are partially applying in the abstract sense; if we pretend we still have a (lambda (x y z) ...) in our abstract semantics, then when we pass a value for x to the curried function, we then have effectively partially applied this abstract function, resulting in something that needs just the (y z) arguments now.
Languages that support implicit partial application erase the difference between f x y and (f x) y. That is to say, the programmer doesn't have to know whether f x y is a call two a function f with two arguments, or whether there is a partial application (f x) which binds the x argument, followed by an application of that resulting function to argument y.
Now if the language substrate is based on one-argument functions, under the hood, so that all functions with two or more arguments are implicitly transformed into one argument functions by currying, then partial application is "free", so to speak. The syntax may let you write f x y to call a function of two arguments, but the implementation is actually doing (f x) y.
Thus currying can support partial application in a similar way to how transformation of a program to CPS (continuation passing style) supports continuations. Partial application is "free" in a curried program similarly to how continuations are "free" in a CPSed program. Under CPS, you already have the return continuation as a hidden argument, so call/cc does nothing more than reveal what is hidden. Under currying, you already have a lambda that takes just one argument, and gives you a lambda that takes the next one.
Are you being serious? The fact that closures and objects are dual is a frequently made observation. It’s not something the poster invented and it’s not (usually) controversial.
Instead of assuming you know better and jumping in with a correction, could you reread that post and assume that the person knows what they're talking about but isn't communicating exactly the way you would have communicated it? I.e., read with the intent of understanding intent, rather than read with the intent of correcting?