dotnet / roslyn

The Roslyn .NET compiler provides C# and Visual Basic languages with rich code analysis APIs.
https://docs.microsoft.com/dotnet/csharp/roslyn-sdk/
MIT License
19k stars 4.03k forks source link

[Proposal] Make open generic type second-class citizen (instead of being third-class) #7644

Closed BreyerW closed 7 years ago

BreyerW commented 8 years ago

By third-class i mean open generic types have very limited use in c#, effectively only in typeof. I tried to find similar proposal but found only one very similar but not same and was named Higher kinded polymorphism. I would like to extend use cases to:

But still abandon use with new keyword (thats why 'second-class citizen') like var foo=new Foo<,>() which makes sense only with inferring generic types which effectively mean closed generic type but that is resolved for us by complier (and this is only another proposal atm from another person)

I think the biggest benefit would be for third party lib provider.

For example I recently wrote very simple 3d engine with 3d view. After implementing very basic features such as displaying 3d object i started small research how to enable custom vertex written by someone else which mean permutations of possible vertex combinations wasnt know ahead of time. To enable this i rewrote Mesh class to generic version and everything work until i started to rewrite code for placing object in 3d view to generic version.

There i dont know vertex format ahead of time! In fact this isnt huge problem since i dont have to know exact format before placing just need to be sure that vertex format is struct and implement one interface which is guarded by generic constraint.

The problem is, C# doesnt allow rely only on generic constraints, you are forced to specify exact type for generic. So i had to constraint myself to one default vertex format and use `dynamic` (which effectively mean giving up performance and type safety) while placing and do some voodoo magic after that, so vertex format can be changed later, or write additional interface and force theorethical client to write bridge for himself. Or rethink approach completely

If i could cast given object to open generic type this could be solved to one line

MeshRenderer<,> meshRenderer=entity as MeshRenderer<,>; //first generic for indices, second for vertex format

and later use only members allowed by generic constraint

Side effect of this feature could be pseudo intersection type (atm for interfaces and abstracts only but after adding traits - for traits too) based on generic constraint. For example you could define open generic field in foo class like:

class Foo{

public OpenGeneric<> openGenericField;

}
class OpenGeneric<T> where T: class, IEquatable, IComparable, IConvertible{
//some stuff
}

and now you could pass to openGenericField every OpenGeneric class with underlying T that are comparable, convertible and equatable and acess only method/members that are guaranteed by these three interfaces (and object itself)

jveselka commented 8 years ago

In some cases covariance could help

interface IMeshRenderer<out TIndices, out TVertex> { }
//...
IMeshRenderer<object, object> meshRenderer = (IMeshRenderer<object, object>)entity;

I don't know how to make it work with value types though.

But generally unbound and partially bound generics is something that C# could improve. I have also found myself using dynamics in its place.

LokiMidgard commented 8 years ago

I like this idea. If I understand it correctly it reminds me of the <? extends something> in Java. actually the only part in Generics in C# that I miss (coming from java).

alrz commented 8 years ago

@BreyerW I think, this needs "intersection types" (#4586) then if you declare OpenGeneric<> all Ts would be considered as an intersection type of all constraints. but I'm not sure about class constraint.

BreyerW commented 8 years ago

EDIT: as second-thought i think i might misunderstanded you: you are talking about that in order to support my request, intersection types have to be implemented? Hopefully no, since i'm thinking about it as just general/unbound (no exact type provided) generic constraint and extended open generic type which already exist but in very limited form


@alrz as i already said side effect of this proposal would be intersection type. However in your linked proposal that seems to be concerned about if statement while my proposal is more general (you could use them in classes/struct as fields/property, return them from method etc., and have some extra features that probably never make into 'normal' intersection types like 'intersect' class or struct or new() or possible in future unmanaged - this may be solved if we choose etc. - im especially concerned about struct since graphic libraries quite often require to be struct). And - to be honest - proposal is in first place about open generic type not about intersection type which is just nice side effect. (that they look very similar is another story).

Keep in mind - with open generic type you work on exact class (which may contain several generic fields/props and generic methods) while true intersection type is more about interface (and later, traits) composition. I think this is main difference between them.

gafter commented 7 years ago

We are now taking language feature discussion on https://github.com/dotnet/csharplang for C# specific issues, https://github.com/dotnet/vblang for VB-specific features, and https://github.com/dotnet/csharplang for features that affect both languages.