Skip to content
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

Propose compiler support for currying/partial application of delegates/methods. #15575

Closed
martaver opened this issue Nov 29, 2016 · 6 comments
Closed

Comments

@martaver
Copy link

Currying or partial application (PA) in C# can be achieved quite handily with a few extension methods, such as:

public static Func<T2, T3, TOut> Apply<T1, T2, T3, TOut>(this Func<T1, T2, T3, TOut> func, T1 t1)
{
	return (t2, t3) => func.Invoke(t1, t2, t3);
}

However, this is only usable where the generic types of the parameters and return values are exposed via the set of Func or Action delegates. If I have a delegate with the equivalent signature, I can't apply either technique.

It would be useful to be able to convert a delegate/method to it's equivalent signature as a Func/Action to be able to apply currying or PA. Ideally, C# would expose some syntax for them and the compiler would handle the creation of corresponding closures. e.g.

/// <summary>
/// Really valuable, context relevant documentation that you don't get when using Func/Action.
/// </summary>
/// <param name="p1">Some vital fact about p1.</param>
/// <param name="p2">Important information about p2.</param>
/// <param name="p3">Handy hints about p3.</param>
/// <returns>Useful tidbits about the returned int.</returns>
public delegate int MyDelegate(int p1, int p2, int p3);

public static class MyClass
{
	public static MyDelegate Foo = ...;

	public static void Main()
	{
		var curryFoo = Foo.Curry;
		var curryOut = curryFoo(1)(2)(3);

		var partialFoo = Foo.Partial(1);
		var partialOut = partialFoo(2, 3);
	}
}

Bonus points for making it possible for IDEs to display the documentation of the originating delegate/method for the curried and partially applied instances.

I realize this feature is probably not high on the priority list, but there's been a lot of syntactical sugar added to C# lately, so I thought I'd ask. Maybe this is low hanging fruit.

It would open up some pretty useful functional approaches to coding in C#.

@HaloFour
Copy link

This sounds like it would depend on #3990. Otherwise you'd need to define those intermediate delegate types. The compiler doesn't currently have any special awareness of the Action<...> or Func<...> delegate families. Those delegates also don't support ref or out parameters.

The CLR doesn't currently support signature equivalence for delegate types either. "Converting" from MyDelegate to Func<int, int, int, int> involves wrapping the one delegate with the other and doubling the (relatively little) overhead.

Somewhat related: #4534

@DavidArno
Copy link

DavidArno commented Nov 29, 2016

As @HaloFour mentions, the CLR doesn't allow conversion between different delegates with identical signatures. However, support around this could be added via the compiler in a trivial fashion (admittedly still needing that wrapping to occur).

For example, when encountering a new non-void delegate type:

public delegate int MyDelegate(int p1, int p2, int p3);

The compiler could automatically emit a new static class with extension methods:

public static MyDelegateExtensions
{
    public static Func<int, int, int, int> ToFunc(this MyDelegate del) => 
        new Func<int, int, int, int>(del);

    public static MyDelegate ToMyDelegate(this Func<int, int, int, int> func) => 
        new MyDelegate(func);

    public static Func<int, Func<int, Func<int, int>>> Curry(this MyDelegate del) => 
        (x) => (y) => (z) => del(x, y, z);

    public static Func<int, Func<int, int, int>> Partial(this MyDelegate del) => 
        (x) => (y, z) => del(x, y, z);
}

Likewise, the same could be done for void delegates, providing Action's instead. And currying/partially applying out and ref parameter delegates doesn't sound very logical to me, so they could possibly be ignored.

Thus for any delegate defined, conversions between in and Func/Action and curry/partial application features could automatically be supplied by the compiler.

And even if this were something that the compiler team wouldn't want to implement, if Source Generators get implemented, and implemented well (ie, not just for partial classes), then this is something that could very easily be added to the language using a generator that detected delegates and emitted that static class in a new "paired" file.

@HaloFour
Copy link

if Source Generators get implemented, and implemented well

Source generators don't require partial classes. That's only necessary if the generated source is going to emit a partial class in order to add/replace existing members.

And you only cared about the Func<...> and/or Action<...> family of delegates then you wouldn't need any additional compiler support, just a bunch of extension methods.

@martaver
Copy link
Author

martaver commented Dec 9, 2016

I may be misunderstanding how Source Generators work - but if they're only being applied at compile time, then how is that going to affect the developer experience?

When using the proposed currying & partial application, any corresponding intellisense options (including documentation from the originating function) should be available to the developer as they type. It's of limited use to developers if they can only use it in a similar fashion to T4.

@DavidArno
Copy link

DavidArno commented Dec 9, 2016

@martaver,

If you have the stamina, read through #5561 (especially toward the end). The language team's position (or at least @CyrusNajmabadi's position) is that it only makes sense for Microsoft to do Source Generators if they are built into the entire "IDE experience" (my words). In other words, those generators would have to work with the IDE's intellisense, error detection etc etc. Assuming they happen (as it sounds a lot of work & other stuff, eg full-blown pattern matching ought to take priority), then my example generator would be sitting there in the background generating those extension methods as you type, such that eg auto-complete knows about them.

@CyrusNajmabadi
Copy link
Member

Closing this out. We're doing all language design now at dotnet/csharplang. If you're still interested in this idea let us know and we can migrate this over to a discussion in that repo. Thanks!

@CyrusNajmabadi CyrusNajmabadi closed this as not planned Won't fix, can't repro, duplicate, stale Nov 8, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants