Roslyn: Proposal: Language support for currying

Created on 13 Aug 2015  Â·  10Comments  Â·  Source: dotnet/roslyn

About currying: https://en.wikipedia.org/wiki/Currying

Since C# 6 introduced expression bodied members, it made a huge step towards more comfortable currying:

``` C#
Func Substract(int minuend) => (int subtrahend) => minuend - subtrahend;

``` C#
var five = Substract(10)(5); 
var substractFromFive = Substract(5); 
var one = substractFromFive(4);

But still there are a few rough edges:

Motivation

  1. Declaration gets complicated fast, especialy nested delegates in return type:
    Func<int, Func<int, int>> Multiplicate(int mul1) => (int mul2) => (int mul3) => mul1 * mul2 * mul3;
  2. Calling is a bit awkward with consecutive parenthesis blocks: var product = Multiplicate(2)(3)(4);
  3. Performance-wise delegate instances are allocated when they don't need to be. Compiler could probably optimize previous example and avoid delegate instantiation entirely.
  4. C# already embraced many functional principles and features. Syntactic support for currying sounds like expected next step which could bring this technique into mainstream use.

    Proposal

Therefore I propose that following should be allowed:

``` C#
//ommited arrows between parenthesis and return type defined as for last method in chain
int Substract(int minuend)(int subtrahend) => minuend - subtrahend;

which would behave like:

``` C#
int Substract(int minuend, int subtrahend) => minuend - subtrahend; 
Func<int, int> Substract(int minuend) => (int subtrahend) => Substract(minuend, subtrahend);

It seems important to note that:

  • Curried functions should be allowed to have parameter blocks of various size:

``` C#
bool Compare(Func comparer)(T item1, T item2) => comparer(item1, item2);
var objectEqualityComparer = Compare((o1, o2) => object.Equals(o1, o2));
var objectEqualityComparerWithA = ObjectEqualityComparer("A"); //error
bool areEqualsAand5= ObjectEqualityComparer("A", 5); //OK

- Results from partial call (call with subset of parameter blocks) to curried function should be again callable as curried function (with multiple parameter blocks in single parenthesis):

``` C#
int Multiplicate(int mul1)(int mul2)(int mul3) => mul1 * mul2 * mul3; 
var multiplyBy5 = Multiplicate(5); 
var thirty = multiplyBy5(2, 3);
  • Curried functions can have regular block bodies, I use expression syntax in examples only for its convenience.

``` C#
int Substract(int minuend)(int subtrahend) { return minuend - subtrahend; }

# 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)` to `Multiplicate(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 trailing `Action<...>` 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).

``` C#
[AttributeUsage(AttributeTargets.ReturnValue)] //or AttributeTargets.Method ? 
public class CurryAsAttribute : Attribute 
{ 
    public CurryAsAttribute(Type type); 
} 

``` C#
//forces ExceedsThreshold(10) to return Predicate instead of Func
[return: CurryAs(typeof(Predicate))]
bool ExceedsThreshold(int threshold)(int value) => value > threshold;

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.

``` C#
[AttributeUsage(AttributeTargets.ReturnValue)] 
public class CurryAsAttribute : Attribute 
{ 
    public CurryAsAttribute(params Type[] types); 
}

C# //forces Compare<T> to return Func<T,Predicate<T>> [return: CurryAs(typeof(Func<,>), typeof(Predicate<>))] bool Compare<T>(Func<T, T, bool> comparer)(T item1)(T item2) => comparer(item1, item2);

This would construct the actual return type starting from the right:

  1. Last parameter block returns bool (from method signature), therefore next to last parameter block should return bool-returning delegate
  2. Second parameter block is marked as Predicate<> which returns bool so its fine. If it were something like Func<,> then bool would be supplied as second type parameter. Predicate type parameter is inferred from T item2 resulting in Predicate<T>
  3. First parameter block is marked as Func<,> so previously fixed Predicate<T> is supplied as second type parameter. First type parameter is inferred from T item1 resulting in Func<T,Predicate<T>>

Other related proposals: #3171

Area-Language Design Resolution-Won't Fix

All 10 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.

Therefore I propose that following should be allowed:

//ommited arrows between parenthesis and return type defined as for last method in chain
int Substract(int minuend)(int subtrahend) => minuend - subtrahend;

Personally I'd prefer something like this:

curried int Substract(int minuend, int subtrahend) => minuend - subtrahend;

The curried modifier would tell the compiler to generate code for a curried function.

This seems like a lot of (language and compiler) work and spec complexity for a small benefit.

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?

@thomaslevesque

In F# the callee is unaffected. let subtract minuend subtrahend = minuend - subtrahend; is still a single function that accepts two arguments. If the caller omits arguments then F# automatically emits a closure class which captures the specified arguments and has an Invoke method for the remaining arguments. For methods that originally accept more than two arguments you can continue to omit arguments and it chains those closure classes together.

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);

In F# the callee is unaffected. let subtract minuend subtrahend = minuend - subtrahend; is still a single function that accepts two arguments. If the caller omits arguments then F# automatically emits a closure class which captures the specified arguments and has an Invoke method for the remaining arguments. For methods that originally accept more than two arguments you can continue to omit arguments and it chains those closure classes together.

Ah, I see. I thought the currying was done at the callee, not the caller.

If C# were to implement automatic currying it would seem more appropriate to do what F# does and implement it at the caller

Yes, I guess that makes sense.

But is something like that really much more of an improvement over the current implementation?

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:

Console.ReadLine()
|> Foo(..., 42)
|> Bar("blah", ...)
|> Baz(...);

@gafter
About small benefit.
AFAIK, functional composition is defined as f(g(x)), and in that definition, functions with more than one argument have no place. Currying is essential for function composition. Currying is decomposition into argument-function elements. Once one have basic elements, it becomes easy to compose them and decorate such a argument-function elements with ie. null-check, inject cross-cutting concerns ....

I'm not guru for functional programming, so please correct me if I'm wrong.

@thomaslevesque

Also note that in F# you can only curry with let-bound functions. Those functions also do not support overloading. F# type functions can be overloaded but do not support currying.

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.

Was this page helpful?
0 / 5 - 0 ratings