Skip to content

[proposal] Support anonymous delegate type #16488

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

Closed
Thaina opened this issue Jan 13, 2017 · 23 comments
Closed

[proposal] Support anonymous delegate type #16488

Thaina opened this issue Jan 13, 2017 · 23 comments

Comments

@Thaina
Copy link

Thaina commented Jan 13, 2017

I think the popularity usage of Action<> and Func<> is a proof that most of the time we don't like to declare type of generic and actually it should not needed

But Action<> and Func<> is hard to declare complex delegate. Especially function with ref and out as parameter or return type

So I think we should be able to always get anonymous delegate automatically from any method and lambda expression

Internally this feature would generate Action/Func or new delegate type automatically at compile time

static function is easy

var parse = int.Parse;
var value = parse("0");

// Translate into

Func<string,int> parse = int.Parse;
var value = parse("0");

Like so

var tryParse = int.TryParse;
tryParse("0",out var value);

// Translate into

delegate _ActionRefOut<T,P>(ref T obj,out P p1);

_ActionRefOut<string,int> tryParse = int.TryParse;
tryParse("0",out var value);

instance function might be a bit tricky. the below does not work for struct

struct Matrix4x4 { void RotateY(float degree){...} }

var rotateY = matrix.RotateY;
rotateY(90); // matrix does not rotate

So I think maybe need to implement like this

struct Matrix4x4 { void RotateY(float degree){...} }

// Actually it would be cleaner if we could get method declaration of type from its type
// Like `var rotateY = Matrix4x4::RotateY; // :: to get instance method`

var rotateY = matrix.RotateY;
// rotateY is delegate(ref Matrix4x4 matrix,float degree) instead of Func<Matrix4x4,float>
rotateY(ref matrix,90);

// At compile time it translate into this instead

delegate _FuncRef<T,P>(ref T obj,P p1);
public static _Matrix4x4RotateY(ref Matrix4x4 matrix,float degree)
{
	matrix.RotateY(degree);
}

_FuncRef<Matrix4x4,float> rotateY = _Matrix4x4RotateY;
rotateY(ref matrix,90);
@Thaina Thaina changed the title Anonymous delegate type? Support anonymous delegate type Jan 13, 2017
@Thaina Thaina changed the title Support anonymous delegate type [proposal] Support anonymous delegate type Jan 13, 2017
@iam3yal
Copy link

iam3yal commented Jan 13, 2017

Related #3990

@DavidArno
Copy link

As an aside, this suggestion has triggered a thought: what's the plan for generic delegates and ref returns? If I have eg,

ref T F<T>(T p) => ...

I assume I can't assign this to a Func<T> delegate and that either the framework or Roslyn team will need to produce a new set of a ref return delegates? Or have I got this wrong?

@iam3yal
Copy link

iam3yal commented Jan 13, 2017

@DavidArno I guess it's the same issue when passing something by ref, you can see @gafter comment about it here so yeah we would probably need to have yet another delegate types for these situations and my guess, this is the reason or some of the reasons they haven't added it yet.

@Thaina
Copy link
Author

Thaina commented Jan 13, 2017

@DavidArno That's why I was proposing transpile to generate delegate automatically to map what we need

Something such

delegate void _ActionRef<P>(ref P param);
delegate R _FuncRef<R,P>(ref P param);
delegate ref R _RefFunc<R>();
delegate ref R _RefFuncRef<R,P>(ref P param);

and alike to map automatically

@iam3yal
Copy link

iam3yal commented Jan 13, 2017

@Thaina This won't work across assemblies, again, see @gafter comment about it.

@Thaina
Copy link
Author

Thaina commented Jan 13, 2017

@eyalsk One way to make it work is to generate all possible combination (at least 8 parameter, same as Action<> and Func<>) hidden in System namespace under corefx

For something weirder than 8 parameter could be unsupported

@iam3yal
Copy link

iam3yal commented Jan 13, 2017

@Thaina Yes, this would probably work but if the CLR would allow the unification of types then if I read what @gafter said correctly it would mean that they would have the ability to generate these types instead of tying the compiler to specific types and this might work with any delegate type.

@iam3yal
Copy link

iam3yal commented Jan 13, 2017

@Thaina Look here, I'm not sure why he wrote function types (no issue yet) I hope that they will have a look at it at some point.

@DavidArno
Copy link

@eyalsk,

Thanks. I'll raise a new issue about that topic therefore as it isn't really related to this one.

@jveselka
Copy link

@Thaina

One way to make it work is to generate all possible combination (at least 8 parameter, same as Action<> and Func<>) hidden in System namespace under corefx

Do you realize thats total of 2*((3^1)+(3^2)+(3^3)+...+(3^8)) combinations? :-)

@iam3yal
Copy link

iam3yal commented Jan 13, 2017

@zippec Yeah but unfortunately atm there's no other way to support it without adding these types, there's also no notion of variadic functions.

@Thaina
Copy link
Author

Thaina commented Jan 13, 2017

@zippec Well, at first I think it is 8^3 which is around 512

Still sum3^n where n 1->8 is around 7000

Which, if we just support 4 parameter it then decrease to around 300

@iam3yal
Copy link

iam3yal commented Jan 13, 2017

@Thaina What we really need is variadic generics before adding more of these types.

@HaloFour
Copy link

HaloFour commented Jan 13, 2017

I largely don't think this is needed. If you need a "complex" delegate that can't be covered by the Action<...> and Func<...> then you can write the one-liner to make a new delegate. Without delegate signature equivalence this is what the compiler will be forced to do anyway. Making the compiler synthesize a type like that is always dangerous as any slight difference in a recompilation will be a breaking change. That means that implementation details such as generated name would have to be a part of the language spec.

For local-only delegate instances that problem goes away, but you still have the issue stipulated in #3990 where the lack of signature equivalence means that you end up with a new delegate type which would be incompatible with any parameter you'd wish to pass it to.

@HaloFour
Copy link

@eyalsk Anonymous delegates specifically. Note that I like the idea of inferred and/or simpler delegate type syntax, including int(int,int) add = (x, y) => x + y; or var add = (x, y) => x + y;. The lack of delegate signature equivalence in the CLR remains my sticking point. If the C# team decided that the added overhead of wrapping the delegate was worth the flexibility in the language then my concern would mostly go away, although that's a performance hit that people would have to be aware of.

@iam3yal
Copy link

iam3yal commented Jan 13, 2017

@HaloFour Sorry for deleting my comment after your edit.

Note that I like the idea of inferred and/or simpler delegate type syntax, including int(int,int) add = (x, y) => x + y; or var add = (x, y) => x + y;. The lack of delegate signature equivalence in the CLR remains my sticking point.

I'd really love to have these two features that is a delegate type syntax and more type inference for both delegates and generics.

p.s. Couldn't post because GitHub was down or something..

@orthoxerox
Copy link
Contributor

orthoxerox commented Jan 13, 2017

@HaloFour

Note that I like the idea of inferred and/or simpler delegate type syntax, including int(int,int) add = (x, y) => x + y; or var add = (x, y) => x + y;

How do you propose the compiler infers the type of the latter delegate? Until we get generic operators, extension operators and concepts it won't be able to infer if it's a delegate on strings, ints or custom structs.

@HaloFour
Copy link

HaloFour commented Jan 13, 2017

@orthoxerox

How do you propose the compiler infers the type of the latter delegate?

To be honest I've not thought it through. The options might be that C# go the F#/Haskell route where the lambda arguments are effectively "generic" (not in the CLR sense) and that the compiler will collapse the definition into an implementation based on use (possibly more than once):

var add = (x, y) => x + y;
var i = add(2, 3); // produces 5
var s = add("Hello ", "World"); // produces "Hello World"

Or the compiler could require that the lambda arguments include type information:

var add = (int x, int y) => x + y;
int result = add(2, 3);

Consider that very uncooked spaghetti, though.

@orthoxerox
Copy link
Contributor

@HaloFour (I have no idea why I mix you two up)

IIRC, Haskell actually infers the type class that provides + for two arguments, unlike F# that inlines the body and lets other parts of the comiler figure the types out.

@Thaina
Copy link
Author

Thaina commented Jan 14, 2017

@HaloFour I really wish we would have that feature you mention. It just need to require CLR support so I just think a way to make it possible without

If CLR can just support that it would be the best. To be honest I would like to write a function type just where it need without naming it. Such as function parameter, if it would use only in one place

void LoadSomething(string where,void(Stream) callBack)
{
}

I think delegate is legacy from when the programming trend is to force everything to has type. But now we start moving to anonymous direction (from anonymous object in C# 4 to Tuple in C# 7) maybe we could add anonymous function as a superclass of delegate, like tuple?

@aluanhaddad
Copy link

@Thaina

I think delegate is legacy from when the programming trend is to force everything to has type.

I believe Delegates were specifically designed to provide structural equivalence and thereby provide needed flexibility. Anders Hejlsberg has spoken about this before, using event handlers as a motivating example where structural equality is desirable and matches programmer intuition.
So the forethought was there from the beginning, but I think time, and the degree to which we leverage delegates to perform basic tasks, has demonstrated that even more flexibility is desirable, specifically in the case of function signature types.

But now we start moving to anonymous direction (from anonymous object in C# 4 to Tuple in C# 7)

Anonymous types, added in C# 3.0, are very strongly typed. The compiler shares the underlying implementations of equivalent anonymous types between methods within an assembly. But that is just an implementation detail, the real issue, as others have said, as is cross assembly binding.

maybe we could add anonymous function as a superclass of delegate, like tuple?

All delegate types do derive from System.Delegate, but the problem is that this type is lossy.

@HaloFour

To be honest I've not thought it through. The options might be that C# go the F#/Haskell route where the lambda arguments are effectively "generic" (not in the CLR sense) and that the compiler will collapse the definition into an implementation based on use (possibly more than once):

var add = (x, y) => x + y;
var i = add(2, 3); // produces 5
var s = add("Hello ", "World"); // produces "Hello World"

The equivalent F# is actually an error. It is one of the things I find counter-intuitive about automatic generalization. It creates the type eagerly on first use, but only once (I'm not sure what Haskell does).

Or the compiler could require that the lambda arguments include type information:
var add = (int x, int y) => x + y;
int result = add(2, 3);

This seems completely reasonable and matches how lambdas currently behave when they are target typed by generics so it would imply no change in the inference of the actual signature.

@Thaina
Copy link
Author

Thaina commented Jan 15, 2017

@aluanhaddad

Anonymous types are very strongly typed

And that's what I always would like it to be in everything. I wish anonymous anything would be strong type. Just that it can be strong without name of the type. The same for anonymous delegate too. I just don't want to name it but it should be compiled as strong type

That's why I talk about superclass, in addition of System.Delegate delegate should have something to map it signature more strongly

@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!

Note: some of this stuff is supported now, thought not all those cases.

@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

9 participants