csharplang/meetings/2021/LDM-2021-02-22.md
2021-07-27 12:34:38 -07:00

5.2 KiB

C# Language Design Meeting for Feb 22nd, 2021

Agenda

  1. Global usings
  2. using alias improvements

Quote of the Day

  • "You're muted" "I wanted to be muted"

Discussion

Global usings

https://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:

  1. Is static a similar modifier? Should it be allowed to come before the using as well?
    • After discussion, we don't believe so. static isn't a modifier on the using, it fundamentally changes what the meaning of the using is about.
  2. We already have existing modifiers for C# scopes: should we use internal as the keyword here?
    • Using 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.

Conclusion

We put global before using, but the proposal is otherwise accepted as is.

using alias improvements

https://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:

  1. If we want to push 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.
  2. If we want to push 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.

Conclusion

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.