Added LDM notes for February 8th, 2021
This commit is contained in:
parent
6f3b971e45
commit
e7fd82ec95
118
meetings/2021/LDM-2021-02-08.md
Normal file
118
meetings/2021/LDM-2021-02-08.md
Normal file
|
@ -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<T>() where T : IHasStatics
|
||||
{
|
||||
int v = T.GetValue();
|
||||
}
|
||||
|
||||
UseStatics<C>(); // C satisfies constraint
|
||||
UseStatics<IHasStatics>(); // 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.
|
|
@ -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)
|
||||
|
|
Loading…
Reference in a new issue