diff --git a/meetings/2021/LDM-2021-02-08.md b/meetings/2021/LDM-2021-02-08.md new file mode 100644 index 0000000..bd57092 --- /dev/null +++ b/meetings/2021/LDM-2021-02-08.md @@ -0,0 +1,118 @@ +# C# Language Design Meeting for Feb 8th, 2021 + +## Agenda + +1. Virtual statics in interfaces + 1. [Syntax Clashes](#syntax-clashes) + 2. [Self-applicability as a constraint](#self-applicability-as-a-constraint) + 3. [Relaxed operator operand types](#relaxed-operator-operand-types) + 4. [Constructors](#constructor) + +## Quote(s) of the Day + +- "If you need to kick yourself I see that [redacted] has a foot in the chat you can kick yourself with" +- "Are we at the point where we derail your meeting with other proposals?" +- "It's the classic, noble art of retconning" + +## Discussion + +Today, we want to take a look at a few top-level design decisions for virtual statics in interfaces that will drive further design and implementation +decisions for this feature. + +### Syntax Clashes + +C# 8 brought 2 new features to interfaces: default members, and non-virtual static members. This sets up a clash between static virtual members and +static non-virtual members. In pre-C# 8, `interface` members were always virtual and abstract. C# 8 blurred the line here: private and static members +in interfaces are non-virtual. For private members, this makes perfect sense, as the concept of a virtual private method is an oxymoron: if you can't +see it, you can't override it. For statics, it's a bit more unfortunate because we now have inconsistency in the default-virtualness of members in +the interface. We have a few options for addressing this inconsistency: + +1. Accept life as it is. We'd require `abstract` and/or `virtual` on static members to mark them as such. There is some benefit here: `static` has +meant something for 21 years in C#, and that is not `virtual`. Requiring a modifier means this does not change. +2. Break the world. Make static members in interfaces mean static virtual by default. This gets us consistency, but breaks consumers in some fashion +and/or introduces multiple dialects of C# to support. +3. We could introduce a bifurcation in interfaces with a `virtual` modifier on the `interface` declaration itself. When marked with `virtual`, `static` +members of the interface are automatically `virtual` by default. This is very not C#-like: we require `static` on all members of `static class`es, why +would `virtual interface`s be any different here? And how would you define the existing non-`virtual` members in such an interface? + +Options 2 and 3 also have a question of how they will apply to class members. Due to the size of the changes required we may have to split virtual +static members, shipping with just interfaces in the first iteration and adding class types at a later point. However, we need to make sure that we +design the language side of these changes together, so when class virtual statics are added they don't feel like an afterthought. The second and third +proposals would likely need to have the first proposal for the class version of the feature anyway. While it would be consistent with instance members, +neither of them would totally eliminate the needs to apply the modifiers to interface members. + +#### Conclusion + +We will require `abstract` or `virtual` be applied to a virtual static member. We will also look at allowing these modifiers for intstance interface +methods, even though they are redundant, much like we allow `public` on members today. + +### Self-applicability as a constraint + +`abstract` static members introduce an interesting scenario in which an interface is no longer valid as a substitute for a type parameter constrained +to that interface type. Consider this scenario: + +```cs +interface IHasStatics +{ + abstract static int GetValue(); // No implementation of GetValue here +} + +class C : IHasStatics +{ + static int GetValue() => 0; +} + +void UseStatics() where T : IHasStatics +{ + int v = T.GetValue(); +} + +UseStatics(); // C satisfies constraint +UseStatics(); // Error: IHasStatics doesn't satisfy constraint +``` + +The main question here is what do we do about this? We have 2 paths: + +1. Forbid interfaces with an abstract static from satisfying a constraint on itself. +2. Forbid access to static virtuals with a type parameter unless you have an additional constraint like `concrete`. + +Option 2 seems weird here. Why would a user have constrained to a type that implements an interface, rather than just taking the interface, unless +they wanted to use these methods? Yes, adding an `abstract static` method to an interface where one does not exist today would be a breaking change +for consumers, but that's nothing new: that's why we added DIMs in the first place, and it would continue to be possible to avoid the break by providing +a default method body for the virtual method, instead of making it abstract. + +#### Conclusion + +We choose option 1. + +### Relaxed operator operand types + +Today, C# requires that at least one of the operand-types of a user-defined operator be the current type. This breaks down with user-defined virtual +operators in interfaces, however, as the type in the operator won't be the current type, it will be some type derived from the current type. Here, +we naturally look at self types as a possible option. We are concerned with the amount of work that self-types will require, however, and aren't sure +that we want to tie the shipping of virtual statics to the need for self types (and any other associated-type feature). We also need to make sure that +we relax operators enough, and define BCL-native interfaces in a way, such that asymmetric types are representable. For example, a matrix type would +want to be able to add a matrix and a numeric type, and return a matrix. Or `byte`'s `+` operator, which does not return a `byte` today. Given that, +we think it is alright to ship this feature and define a set of operator interfaces without the self type, as we would likely be forced to not use it +in the general interfaces anyway to keep them flexible enough for all our use cases. + +#### Conclusion + +We're ok with relaxing the constraints as much as we need to here. We won't block on self types being in the language. + +### Constructors + +Finally, we looked at allowing or disallowing constructors as virtual in interfaces. This is an interesting area: either derived types would be required +to provide a constructor that matches the interface, or derived types would be allowed to not implement interfaces that their base types do. The feature +itself is a parallel to regular static methods; in order to properly describe it in terms of static methods, you'd need to have a self type, which is +what makes it hard to describe in today's C# terms in the first place. Adding this also brings in the question of what do we do about `new()`? This may +be an area where we should prefer a structural approach over a nominal approach, or add a form of type classes to the language: if we were to effectively +deprecate `new()`, that would mean that every type that has a parameterless constructor would need to implement an `IHasConstructor` interface instead. +And we would need to have infinite variations of that interface, and add them to every type in the BCL. This would be a serious issue, both in terms of +sheer surface area required and in terms of effect on type loads and runtime performance penalties for thousands and thousands of new types. + +#### Conclusion + +We will not have virtual constructors for now. We think that if we add type classes (and allow implementing a type class on a type the user doesn't own), +that will be a better place for them. If we want to improve the `new()` constraint in the mean time, we can look at a more structural form, and users +can work around the lack of additional constraints for now by using a static virtual. diff --git a/meetings/2021/README.md b/meetings/2021/README.md index 5f402a0..90c9305 100644 --- a/meetings/2021/README.md +++ b/meetings/2021/README.md @@ -43,15 +43,20 @@ First class native integer support - https://github.com/dotnet/csharplang/issues - Global usings - *Triage championed features and milestones* -## Feb 8, 2021 - -- Statics in interfaces - - # C# Language Design Notes for 2021 Overview of meetings and agendas for 2021 +## Feb 8, 2021 + +[C# Language Design Notes for February 8th, 2021](https://github.com/dotnet/csharplang/blob/master/meetings/2021/LDM-2021-02-08.md) + +1. Virtual statics in interfaces + 1. Syntax Clashes + 2. Self-applicability as a constraint + 3. Relaxed operator operand types + 4. Constructors + ## Feb 3, 2021 [C# Language Design Notes for February 3rd, 2021](https://github.com/dotnet/csharplang/blob/master/meetings/2021/LDM-2021-02-03.md)