-
Notifications
You must be signed in to change notification settings - Fork 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]: Support generics and generic type parameters in aliases #1239
Comments
Duplicate of #90. |
This would also help to resolve ambiguity problems as in dotnet/roslyn#24022 (See also this comment in the PR). |
When combined with #218, the generic type parameters of the enclosing scope should be available to the alias. This was discussed a bit on Gitter. Starting from an example on @DavidArno's SuccincT project: internal sealed class ValueOrErrorMatcher<TResult> : IUnionFuncPatternMatcherAfterElse<TResult>, IValueOrErrorFuncMatcher<TResult>, IValueOrErrorActionMatcher, IUnionActionPatternMatcherAfterElse
{
...
IUnionFuncPatternCaseHandler<IValueOrErrorFuncMatcher<TResult>, string, TResult>
IValueOrErrorFuncMatcher<TResult>.Value() =>
new UnionPatternCaseHandler<IValueOrErrorFuncMatcher<TResult>, string, TResult>(RecordValueAction, this);
IUnionFuncPatternCaseHandler<IValueOrErrorFuncMatcher<TResult>, string, TResult>
IValueOrErrorFuncMatcher<TResult>.Error() =>
new UnionPatternCaseHandler<IValueOrErrorFuncMatcher<TResult>, string, TResult>(RecordErrorAction, this);
IUnionFuncPatternMatcherAfterElse<TResult> IValueOrErrorFuncMatcher<TResult>.Else(TResult value)
{
_elseAction = _ => value;
return this;
}
IUnionFuncPatternMatcherAfterElse<TResult> IValueOrErrorFuncMatcher<TResult>.Else(
Func<ValueOrError, TResult> elseAction)
{
_elseAction = elseAction;
return this;
}
...
}
internal sealed class ValueOrErrorMatcher<TResult> : IUnionFuncPatternMatcherAfterElse<TResult>, IValueOrErrorFuncMatcher<TResult>, IValueOrErrorActionMatcher, IUnionActionPatternMatcherAfterElse
{
using IVOEMatcher = IValueOrErrorFuncMatcher<TResult>;
using IUFuncPCHandler = IUnionFuncPatternCaseHandler<IVOEMatcher, string, TResult>;
using IUFPMatcherAE = IUnionFuncPatternMatcherAfterElse<TResult>;
using UPCHandler = UnionPatternCaseHandler<IVOEMatcher, string, TResult>;
...
IUFuncPCHandler IVOEMatcher.Value() => new UPCHandler(RecordValueAction, this);
IUFuncPCHandler IVOEMatcher.Error() => new UPCHandler(RecordErrorAction, this);
IUFPMatcherAE IVOEMatcher.Else(TResult value)
{
_elseAction = _ => value;
return this;
}
IUFPMatcherAE IVOEMatcher.Else(Func<ValueOrError, TResult> elseAction)
{
_elseAction = elseAction;
return this;
}
...
} |
I found myself wanting to do something like this: #if NET30 using Func<TInput, TOuput> = System.Converter<TInput, TOuput>; #endif |
Any chance we will get this in foreseeable future? I ran across this when trying to write bare-bones parser combinator with functions like this: static Func<string[], int, (int next, T result)?> ParseAnyOf<T>(
params Func<string[], int, (int next, T result)?>[] parsers
) The best I can do now is to introduce a struct for Ideally, for a functional style code we should be able to give names to reoccurring function "shapes". using Result<T> = (int next, T value);
using ParseFunc<T> = Func<string[], int, Result<T>?>;
static ParseFunc<T> ParseAnyOf<T>(params ParseFunc<T>[] parsers) => ... |
Not without a proposal Champion. |
Did this answer add something? I can only paraphrase my question: Any chance this will get a proposal Champion? For the amount of requests it's surprising it stays like this. |
Only members of the language team can say for sure, and only really through either championing it or by outright closing it. A lot of proposals end up in this limbo and it's a bit unfortunate but that kind of happens when you have a relatively small team and limited resources but allow the public to pile onto a near infinite backlog. Not to mention the desire to intentionally be very conservative and deliberate about the change accepted into the language. This won't answer your question either, but only someone with a "Member" badge next to their name can give you something closer to an authoritative answer. And even then I wouldn't expect much of a direct answer unless it happens to be, "no." |
That presumably refers to dotnet/roslyn#116. Are aliases going to be overloadable by the number of type parameters? The compiler already allows an alias to have the same name as a type. (If the type is not generic, then the compiler allows the definition, but references may cause error CS0576.) using Chore = System.Threading.Tasks.Task;
using Chore<TResult> = System.Threading.Tasks.Task<TResult>;
partial class Chore<TResult, TProgress> {} Is there any surprising interaction with type inference and overload resolution? It seems the compiler could first expand all type aliases and then use its current algorithms. |
Excellent questions, and ones we need to design and answer 🙂 |
As If an alias does not use some of its type parameters at all ( XML documentation comments for type parameters of aliases can wait until XML documentation comments on non-generic aliases are supported. |
When we look at this we should also look at allowing built-in types, e.g. |
I disagree. I think the built-in types should never be disguised, I cannot think of a single example where that would make the code more clear instead of less. In the example you gave, the programmer should make a new class or struct called |
Kotlin has an experimental "inline class" feature for compile-time-only wrapping of types. So |
I have a REALLY good use case. I have a class that contains several different Well, there's...
But you can't do that with
oh crap, another error? cmon! "using declarations must be made at the top of the file before any other declarations" Ugh. Does C# have macros at all like in C++? Nope. Well then what am I supposed to do, I can't keep changing my code in 15-20 different places just to add a parameter! But no, that's exactly what I have to do. sigh. Why? Why am I not able to do something like...
or
C has had |
There's nothing particularly special about But you can define your own:
I used meaningless bird names because I can't discern what your delegates do ... but I think these are entirely equivalent to what you're wanting to define. No aliases needed. |
Which has the disadvantage that they're not compatible to the "original" delegate type anymore. |
Right. What I'm doing is refactoring several methods with lots of repeated code. So I created a class that does what that code does for me, allowing the user of the class to add custom functionality if they wish, or just change some settings and it will do what they want for each use case. And that's fine but I ran into that problem of "I can't put the type in one spot and change it dynamically while I write this class". I'm trying to make my code more maintainable and manageable which is the whole reason I wanted to alias the types. |
This would be really handy for things like coordinates: public class Coordinates<TRow, TColumn> {...}
// now I could say this but this is inheritance not an alias so it requires consuming code to actually use the subclass
public class Coordinates2<int> : Coordinates<int, int> {...}
public class IntCoordinates2 : Coordinates2<int> {...}
// this would be so much nicer
// the only problem is, would I have to rewrite these using statements in every code file?
using Coordinates2<T> = Coordinates<T, T>;
using IntCoordinates2 = Coordinates2<int>; |
@ekolis see also #3428 Edit: And also #259 / dotnet/roslyn#7451 I guess. |
I see this is not present in the current working set (#4144)? I think it's a necessity to add generics to the discussion when we talk about "global aliases." this would make it tenfold more useful. |
+1 First thing I'd do if this was a feature is:
|
I upvote this. |
Any news about this feature? I'm excepting this on C# |
This is more desirable now that we have a "global using" language feature. |
PLEASE. A legacy monolith I'm working in has a type called
I am forced to fully-qualify the In terms of productivity enhancement this is such a small change that would make such a significant difference to so many C# users. Please, implement it - I beg of you. |
When dealing with subclasses, or smaller types used elsewhere (mainly nodes), I prefer aliasing these types to friendly and short names for sake of convenience. Because of limitations in aliasing, it is impossible to alias a generic without supplying concrete type(s). Was about to request this, seems I've been beaten to the punch by a mile! |
Knowing this discussion is quite old = mature, how about using Opposed to the C# 10 global using directive, which is...
...using |
ITNOA Hi, Are you think, this feature comes to C# 12? thanks |
@soroshsabz No. You can tell this by looking here: |
Named delegate types, as suggested once above, may help in some situations, but hardly in the general case. In case this is left hanging due to technical difficulties, another crutch that could help in a lot more situations would be allowing using sealed classes as type constraints.
Here the exact same type is repeated three times. I can hardly find the method and parameter names. Contrast that with this (which is illegal since Expression is sealed)
|
Just want to add some breadcrumbs here: Open Proposal PR allowing generics in aliases (not in proposals yet): Using alias as a "macro" referenced by the LDM: Recent proposal for allowing more types in aliases (shipped in C# 12): Discussion about the proposal: Relevant part of the standard (v8 draft): My understanding from @CyrusNajmabadi 's Post here is that the current proposal avoids generic forwarding on purpose and assuages a large amount of implementation pain points. |
@HaloFour commented on Fri Jul 17 2015
This is an alternative proposal to #116.
I propose the following changes to aliases to support generics:
* There's probably a better item in the spec for this.
The purpose of this proposal is to allow aliases to be defined for unbound or partially bound generic types where the alias is then treated as a generic type that must be closed at the point at which it is used.
For example the following is legal in C# today:
This expansion would permit the following:
The name of the generic type parameter can be anything. The arguments are matched by their position in the aliased type.
When consuming the alias the rules would apply the same as when consuming a generic type:
Arguments:
This syntax cannot be assembled from existing syntax within the specification thus requiring additional work.
This is true. Even though I provide a syntax above it is likely incorrect and the feature probably requires some new syntax. I believe that this is worth it for the functionality that the feature provides and I also feel that this syntax is more intuitive to the developer than the alternatives.
Requiring the developer to name the generic type arguments is confusing.
Naming generic type arguments is a very common task for anyone who has to write a generic type or generic method today. This should be somewhat second nature. This proposed syntax is very similar to the same syntax that would be required to define a new generic class with a generic subclass.
Requiring the developer to name the generic type argument requires more keystrokes.
This would require more keystrokes than simply using the existing unbound type syntax. However, the developer is not bound to using the same generic type parameter names as the type that is being aliased making the required number of additional keystrokes only one per generic type parameter. Given the common convention for generic type parameters is a single character, e.g.
T
, I don't think that this places an undue burden on the developer defining the alias nor any developer who must read and maintain that alias. It also represent a very small price to pay for the keystroke benefits that the feature would provide.Complications:
using MyList<T> = System.Collections.Generic.List<MyList<T>>;
.The rules for aliases today already solves this problem by not permitting an alias to refer to itself or another alias. If that weren't the case this scenario would already be problematic:
using MyList = System.Collections.Generic.List<MyList>;
2. The alias can represent a partially open generic type which is problematic if the developer attempts to obtain a
Type
instance from the alias viatypeof()
.This is true. Given the
StringDictionary<T>
alias defined above it would be a problem to attempt to obtain aType
reference usingtypeof(StringDictionary<>)
. In my opinion this could be solved in one of two possible ways:Make it illegal to use the unbound type definition of the alias. The expression
typeof(StringDictionary<>)
would be a compile-time error, however the expressiontypeof(StringDictionary<bool>)
would be valid and equivalent totypeof(Dictionary<String, bool>)
. This might be confusing if aliases to an unbound type were supported, e.g.typeof(MyList<>)
.Support partially open
Type
references. This requires a little more code generation but is perfectly legal:I am including this potential solution for the sake of argument. While being able to support syntax
typeof(StringDictionary<>)
would reduce the confusion for the developer I think that same developer would be more confused if the result is a partially open generic type.@HaloFour commented on Fri Jul 17 2015
Alternatively, as an addendum to the syntax proposed by #116 this could be changed to use partially bound type names. If that proposal were implemented this is likely the changes that would need to be made to it in order to add support for partially bound aliases short of having two separate generic alias syntaxes:
@sharwell commented on Fri Jul 17 2015
I don't see this proposal as an alternative to #116, but rather as a separate feature request intended to solve a different problem -- specifically the ability to use partially-bound generic using directives.
@HaloFour commented on Fri Jul 17 2015
@sharwell
Perhaps. Given the overlap between the two I feel that it is probably more appropriate to approach them as alternatives. If #116 were to be implemented (or was already implemented) I don't feel that a completely separate syntax such as this would be appropriate and I would instead be pushing for further evolution of your proposal in a similar vein to the first comment I made to this proposal.
@whoisj commented on Wed Jul 22 2015
@HaloFour 👍 I love this. I would use it constantly.
@aluanhaddad commented on Mon Jul 27 2015
👍 This is an excellent idea.
@zippec commented on Tue Aug 04 2015
I believe there is a problem with proposed syntax:
because there is no way for the compiler (other than convention) to tell if it should look up type TValue or if it is unbound. Thus the syntax in #116 is an obvious choice:
EDIT: I take that back. Of course the compiler can match generic parameter type name from alias to generic parameter type name of aliased type. This will even allow reordering of type parameters.
@HaloFour commented on Tue Aug 04 2015
@jveselka
I had used
TValue
as the name of the generic type parameter just to keep with convention. The name should be anything and the type arguments matched by position. I'll update the proposal to make that more clear. Thanks.@sharwell commented on Tue Aug 04 2015
But the proposed syntax is a reference to an existing type, not the declaration of a new type. In C#, the only way to reference an open generic type in code is by omitting the type arguments (e.g.
typeof(Dictionary<,>)
). This proposal increases developer confusion surrounding an already poorly understood feature by creating a second way to do the same thing.¹By defining the support for aliasing open generic types using the same syntax as
typeof
, we not only address this functionality in the simplest possible form, but we also improve developer familiarity and understanding of the prior feature.¹ I'm ignoring XML documentation comments here, which have their own syntax.
@HaloFour commented on Tue Aug 04 2015
@sharwell
Why does aliasing need to be artificially limited by being intrinsically tied to
typeof
? You don't usetypeof
when defining the alias in any situation, let alone with generic types. The fact that the syntax smells like that used withtypeof
is not relevant, they don't refer to the same grammars in the specification, accidentally or intentionally. You can't aliasvoid
, but you cantypeof(void)
. I see no reason whatsoever to conflate the two. Doing so would only increase developer confusion while simultaneously neutering the functionality of aliasing generic types.This proposal is simply more intuitive and more functional. It solves the common use case of aliasing partially open generic types such as
Dictionary<string,>
that cannot be defined throughtypeof
or your proposal. It doesn't have a byproduct of educating the developers abouttypeof
, and it shouldn't.We're simply going to have to agree to disagree. That is why I submitted a separate proposal.
@paulomorgado commented on Tue Aug 04 2015
Given:
What is
typeof(StringDictionary)
supposed to be?@HaloFour commented on Tue Aug 04 2015
@paulomorgado
typeof(StringDictionary)
would be a compiler error sinceStringDictionary
does not exist.However,
typeof(StringDictionary<>)
is a different story and one that I explicitly mention in the complications section of the proposal. I put out two potential solutions for the sake of argument, but I am definitely in the first camp where the expressiontypeof(StringDictionary<>)
results in a compiler error.@TyreeJackson commented on Thu Aug 06 2015
👍 I also agree that this is an excellent idea. I've often wanted to do precisely what this proposal asks for.
@alrz commented on Thu Dec 03 2015
There is no way to get this out of file scope?
@aluanhaddad commented on Fri Dec 04 2015
@alrz The scope would, l imagine, be consistent with other using directives.
@alrz commented on Fri Dec 04 2015
I mean something like this,
so it'll be in scope in all files.
@HaloFour commented on Fri Dec 04 2015
@alrz Sounds like that should be a separate proposal entirely. I imagine it would be a compiler directive, as it is in VB.NET:
/imports:"Foo = System.Int32"
@alrz commented on Fri Dec 04 2015
@HaloFour I wonder how F# is doing this for type abbreviations like
seq<'T>
; does it expose these aliases, orseq<'T>
is a special case?@HaloFour commented on Fri Dec 04 2015
@alrz The is no concept of aliases in the CLR. Every compiler that supports the notion does so in its own fashion and entirely internally. F# seems to treat
seq<'T>
as an alias forSystem.Collections.Generic.IEnumerable<T>
, which isn't really different from C# treatingstring
as an alias forSystem.String
or VB.NET treatingDate
as an alias forSystem.DateTime
. A custom type alias in F# isn't encoded in any fashion in the resulting assembly anymore than it is in C# or VB.NET.@wizzardmr42 commented on Sun Jan 03 2016
@aluanhaddad It would also be useful to take it across assembly boundaries and use it without creating a named alias, neither of which are do-able with aliasing.
@aluanhaddad commented on Sun Jan 03 2016
@wizzardmr42 That sounds like an orthogonal feature.
@wizzardmr42 commented on Sun Jan 03 2016
@aluanhaddad Sorry - just pointing out that wouldn't be a complete solution for everything discussed in #3281, which referenced this
@HaloFour commented on Mon Jan 04 2016
@wizzardmr42
I was commenting specifically on the mention of the current limitations with type aliases. It was someone else who had suggested that type aliases could be an alternative workaround to the original proposer's problem.
This proposal is only a modest extension of existing aliases in C# and does not touch on the subject of extending them beyond the current code file. @alrz has proposed #7451 to extend aliases throughout the current project using code notation. I personally prefer VB.NET's way of handling that, by using command line arguments to the compiler (exposed through the project configuration) to establish project-wide aliases.
For something beyond the current project there could either be tooling to help configure multiple projects within a solution, or there would need to be a convention of embedding some kind of metadata in the assemblies to declare aliases. That concept seems very messy as it sounds like it would establish a second type system within the compiler that would be erased by compilation and ignored by the CLR.
@wizzardmr42 commented on Mon Jan 04 2016
Sorry - I actually meant to post that on #3281, but it is worth pointing out here too that being able to reference existing types using a "typeof" keyword would solve some of the same problems that this would so may be worth looking at #3281 before implementing this (and #7715 which is a duplicate, but I gave more specific cases where it would be useful there).
@paulomorgado commented on Mon Jan 04 2016
@HaloFour,
One immediate benefit of project-wide vs. solution-wide aliases is the possibilty of shared source files binding to different types according to the project they are being compiled against without having them poluted with conditional compiler directives. Atlthough that would erase the obiousness of the per-project semantics.
@alrz commented on Tue Jan 05 2016
@paulomorgano I didn't think of that! It would be a pleasant workaround for #7451 in case of need for solution-wide aliases.
@TonyValenti commented on Thu Feb 04 2016
Hi All,
I was just reading this and this is something that I would love - especially if it had the ability for me to expose the alias into derived classes.
The example that I deal with a lot is related to refactoring generic classes that inherit from other generic classes. Take this example below:
If I want to change
SpecificImplementation
to inherit fromBaseClass<RefactoringParameterType, U>
, I then have to dig through the source code (and any other classes that inherit fromSpecificImplementation<U>
) and change the method's signature to be:In order to get around that, I'll usually create a type that simply inherits from one of my generic parameters. For example:
When I do this, all I'm really doing is creating an alias for T that gets propagated down into child classes. In most cases, I really prefer that because I don't have to deal with giant generic definitions and it makes refactoring possible, however, the one downfall of doing things the way that I am is that
MyParameterType
is not assignable from a<T>
.I love a construct that would allow me to create a true alias for a type and pass that down into derived or other classes.
For example, here is a thought:
@aluanhaddad commented on Thu Feb 04 2016
@TonyValenti While that is certainly a scenario I would like to see supported, it seems like a perfect use case for a custom-refactoring, and not a language feature. This proposal is nicely focused and scoped (no pun intended), adding significant value while impacting little else in the language. That said, I think the scenario you mention is very compelling and I would find it very useful, but it could be a refactoring. That said, if it naturally falls out of this proposal, so much the better.
@dsaf commented on Thu Sep 15 2016
The resemblance is eerie: https://github.com/apple/swift-evolution/blob/master/proposals/0048-generic-typealias.md
The text was updated successfully, but these errors were encountered: