-
Notifications
You must be signed in to change notification settings - Fork 4.1k
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
Proposal: Language support for currying #4534
Comments
Why does this need a new syntax or CLR changes? Why should the function have to be declared in a different manner to support currying? I already use currying and partial application very frequently just with lambdas at the consumer. public int Substract(int minuend, int subtrahend) => minuend - subtrahend;
///
Func<int, int> subtractBy5 = (minuend) => Subtract(minuend, 5);
Func<int> subtract10By5 = () => subtractBy5(10);
int result = subtract10By5();
Debug.Assert(5 == result); Is there a reason that this method is insufficient? Syntactically being able to infer the delegate types would be nice although that has its problems. |
This seems like a lot of (language and compiler) work and spec complexity for a small benefit. |
Personally I'd prefer something like this:
The
I've learned to love currying in F# and other functional languages, and I think it would be great to have it in C#; so I disagree that it would have a small benefit. But I agree that it would probably be difficult to make it fit in the language; the "obvious" approach of returning a delegate is too inefficient. How does F# handle this, anyway? |
In F# the callee is unaffected. This isn't really different than what I mentioned above, using lambdas and delegates for currying. F# uses a common set of generic base classes and a virtual invoke, C# would use a delegate invoke, but the two should be very similar in performance. If C# were to implement automatic currying it would seem more appropriate to do what F# does and implement it at the caller. Provide a more succinct syntax to indicate that you're intentionally calling a method with fewer than required arguments (giant can of worms with overloads) and the compiler automatically wires up the closures/delegates/what-have-yous: // Call Subtract but only pass minuend, compiler automatically emits
// closure class and assigns delegate
Func<int, int> curried = Subtract(10, ...);
int result = curried(5); But is something like that really much more of an improvement over the current implementation? Func<int, int> curried = (subtrahend) => Subtract(10, subtrahend);
int result = curried(5); |
Ah, I see. I thought the currying was done at the callee, not the caller.
Yes, I guess that makes sense.
Well I think it would make for more readable code. Basically it would just be shorthand for a lambda expression. I think it would be especially useful in combination with #5445:
|
@gafter I'm not guru for functional programming, so please correct me if I'm wrong. |
Also note that in F# you can only curry with |
I think "closed because it's way too much work and would make C# much more complex" is a valid reason to close this request. But the comment about "small benefit" is a bit strange. Currying and partial application works wonderful in other language and the theory is sound. Many people see it as an huge advantage in their daily work. |
@HaloFour I think the fact that overloading in F# isn't allowed in those circumstances is more because of the complexities of type inference rather than partial application (although I might be wrong here). |
Declaring the return type on functions/methods with curried parameters is very cumbersome. This is not a small improvement as it alleviates a big pain for those of us who are comfortable with currying. |
About currying: https://en.wikipedia.org/wiki/Currying
Since C# 6 introduced expression bodied members, it made a huge step towards more comfortable currying:
But still there are a few rough edges:
Motivation
Func<int, Func<int, int>> Multiplicate(int mul1) => (int mul2) => (int mul3) => mul1 * mul2 * mul3;
var product = Multiplicate(2)(3)(4);
Proposal
Therefore I propose that following should be allowed:
which would behave like:
It seems important to note that:
Implementation
Unfortunately, this can't be solved by simply generating more overloads for curried methods because their number grows exponentially. So maybe mark curried methods with some attribute and make jitter take care of that, but I assume that would require CLR support which would be unfortunate. Maybe give up performance improvements and memory savings and simply translate calls
Multiplicate(5, 2, 3)
toMultiplicate(5)(2)(3)
but that would discard one of the motivations for this proposal – performance improvements.Clearly this needs insight from someone smarter than me, with deeper knowledge of CLR.
Generated delegate types
Since delegate type declaration is omitted from curried function declaration, it raises a question which types should be generated by the compiler. Implicit delegate conversions as mentioned in #14 would solve this issue, but it seems it has fallen out of favor.
Therefore I would propose for delegates to be combined by default from standard
Func<...>
and possibly single trailingAction<...>
if the whole function returns void.However this behavior should be configurable if necessary. I have thought of two possible solutions, both utilizing attributes (targeting either Method or ReturnValue).
Option 1
Attribute constructed with single parameter containing Type describing return type of function as called with only first parameter block (i.e. exactly the same you would have to write without currying syntax support).
This approach would be much more complex with generic curried functions. It would probably require partially bound generics as (more or less) discussed in #3993.
Option 2
Attribute constructed with list of unbound Types, each describing return type of one parameter block.
This would construct the actual return type starting from the right:
Predicate<>
which returns bool so its fine. If it were something likeFunc<,>
then bool would be supplied as second type parameter. Predicate type parameter is inferred fromT item2
resulting inPredicate<T>
Func<,>
so previously fixedPredicate<T>
is supplied as second type parameter. First type parameter is inferred fromT item1
resulting inFunc<T,Predicate<T>>
Other related proposals: #3171
The text was updated successfully, but these errors were encountered: