meetings/2021/LDM-2021-02-22.md
usingshttps://github.com/dotnet/csharplang/blob/master/proposals/csharp-10.0/GlobalUsingDirective.md
Today we discussed the proposed syntax and restrictions on the current feature specification. As checked in today, the proposal
puts global after the using directive, which could be potentially ambiguous and require complicated parsing logic, as global
is a valid namespace identifier today. For example, this is valid code:
// Is this a global using alias, or a top-level using variable declaration?
using global myVar = Test.AGlobal;
class Test
{
public static global AGlobal = new global();
}
class global : IDisposable { public void Dispose() {} }
We could potentially resolve this in the same way we did for the record keyword, which is to forbid types be named global in
C# 10. We haven't gotten pushback on this approach for record as a type name so far, which is positive. However, aside from the
ambiguity, there are other reasons to view global-first as a good thing. global here is a modifier on the using, which C#
generally puts before the declaration, not after. We also considered 2 separate but related questions:
static a similar modifier? Should it be allowed to come before the using as well?
static isn't a modifier on the using, it fundamentally changes what the meaning
of the using is about.internal as the keyword here?
internal begs the followup question: what does public on one of these usings mean? What about private? We're
uncomfortable with the idea of treating these more like types than like macros. This starts to get into a very slippery slope
of how we export aliases publicly, can additional constraints be added to aliases, and how does that interact with roles?Finally, we discussed the proposed restrictions on the feature. We like them overall: if global and non-global usings can
be interspersed, it could lead to user confusion as to whether regular using directives can affect global ones. It also helps
set up a clearly-defined set of scopes. We add a new global scope that comes before all top-level using statements today, and
their interactions mirror the interactions of regular usings statements with those nested in a namespace.
We put global before using, but the proposal is otherwise accepted as is.
using alias improvementshttps://github.com/dotnet/csharplang/pull/4452
Continuing with the theme of using improvements, we also discussed generalized improvements to using aliases to address a
number of reoccuring complaints over the years with limitations in today's approach. Specifically, using directives today
cannot reference predefined names like string, they can't have generic type arguments, they can't use tuple syntax, or use
pointer types (including the C# 9 function pointer syntax). The current proposal allows all of these things, including aliases
for partially-bound type parameters such as using MyDictionary<T> = System.Collections.Generic.Dictionary<int, T>;. When
combined with the previous global using feature, enabling this would allow compilation-unit type aliases. Our remaining open
questions here focus again on how we want to push usings forward in the future:
using aliases as real types, then they need to have the ability to expression the same things all other
types can, including constraints and variance. If we go this route, we could potentially have a future where you can introduce
an alias that adds a new constraint onto an existing type, such as an invariant IEnumerable<T> alias, or a dictionary that
is constrained to only struct-type values.using aliases as more macro-expansion, then the ability to expression constraints and variance is a
confusing detriment. The type parameter will be substituted at expansion site and we'll error at that point if an illegal
substitution is made.Approach 1 has many followup questions that quickly start to slide into roles territory. Aliases can be used in public API, for
example, so how would we advertise them publicly if the alias has added new constraints? Is it possible for users to introduce
an opaque alias, such as aliasing int to CustomerId in such a way as there's no implicit conversion between them? Ultimately,
we think that these problems are better solved by a discrete language feature such as roles, rather than as an expansion on
using aliases.
using aliases will be treated more as macro expansions than as real types. We'll check substitutions for concrete types where
we can (such as erroring in the alias itself for using MyList = System.Collections.Generic.List<int*>;), but for things we
cannot check at the declaration site, we will error at the use site. There is no way to add constraints to a using alias.