Add design notes

This commit is contained in:
Mads Torgersen 2017-06-01 17:50:56 -07:00
parent ed7d0d86b0
commit 325012c654
2 changed files with 37 additions and 31 deletions

View file

@ -1,57 +1,54 @@
# C# Language Design Notes for Apr 19, 2017
*Quote of the day*: "I'm not thrilled with the decision, but it was a constrained call"
***Raw notes, yet to be cleaned up - read at your own peril***
## Agenda
1. Improved best common type
2. Diamonds with classes
3. Structs and default implementations
4. Base invocation
"I'm not thrilled with the decision, but it was a constrained call"
# Improved common type
# Improved best common type
``` c#
b ? null : 7 // should be int?
b ? null : 7 // should be int? not error
```
This is a small, isolated change that leads to a nice improvement. Let's do it!
# Diamonds with classes
The class should always win. It's implementation in a class is more specific. "Does the class implement this member? No, then look for a default implementation".
A class implementation of an interface member should always win over a default implementation in an interface, even if it is inherited from a base class. Default implementations are always a fallback only for when the class does not have any implementation of the member at all.
# structs and default implementations
Very hard to use default implementations from structs.
# Structs and default implementations
Referring to the IB example in the issue.
Only way:
It is very hard to use default implementations from structs without boxing. In general, the only way to use interface methods on a struct without copying or boxing is through generics and ref parameters:
``` c#
public static void Increment<T>(ref T t) where T : IB => t.Increment();
public static void Increment<T>(ref T t) where T : I => t.Increment();
```
Even that won't necessarily avoid boxing! What is the type of `this` in the implementation of `IB.Increment`? If it's `IB`, then `this` is a boxed form of the struct! We can avoid that by having an implicit type parameter, a "this type", which is constrained by `IB`. But then, if that doesn't have a name, you can't use it in the body. If it does have a name, do we need a syntax for that?
But for default implementations, even that won't necessarily avoid boxing! What is the type of `this` in the default implementation of `I.Increment`? If it's `I`, then `this` is a boxed form of the struct! We can avoid that by having an implicit type parameter, a "this type", which is constrained by `I`. But then, what is the name of that implicit type parameter in the body? If it doesn't have a name, you can't use it in the body. If it does have a name, what is the syntax for that?
For inherited members on structs today, they do in fact get boxed, but it is never observable - because of the behavior of those three methods (`ToString`, etc.).
For inherited members on structs today, they do in fact get boxed, but it is never observable - because the behavior of those three methods (`ToString`, etc.) doesn't mutate, or otherwise reveal the boxing.
We can't do 1. - it would undermine the value of the feature. (Because adding a new interface member with a default implementation would break implementing structs).
Number 2. pushes the problem elsewhere, at a high cost and with a high risk that you box later anyway.
Number 3. looks like a cop out but there really doesn't seem like a good alternative. C# already has places where structs and interfaces behave weirdly together.
Can we make it so that structs *inherit* default interface members? That would almost certainly make it breaking.
What can we do?
1. *Forbid structs to make use of default implementations*: Can't do that. Now adding a new interface member with a default implementation would break implementing structs.
2. *Come up with some code gen strategy like implicit this types*: Pushes the problem elsewhere, at a high cost and with a high risk that you box later anyway.
3. *Don't worry about it, and leave it as a wart in the language*: It looks like a cop out, but there really doesn't seem to be a good alternative. C# already has places where structs and interfaces behave weirdly together; this just adds to the pile.
## Conclusion
Let's stick with 3. It would be a breaking change to do 2 later, so we can never change it.
Let's stick with option 3. It would be a breaking change to do 2 later, so we can never change it.
# Base invocation
Like classes it should generate a non-virtual call.
What should be the syntax for base invocation? Some candidates:
``` c#
I.base.M()
@ -59,11 +56,12 @@ base(I).M()
base<I>.M()
```
`base(I)` is the winner. It works like `default(T)`.
`base(I)` is the winner. It reads like `default(T)`, and seems the best fit.
Should these be permitted in classes too, to get base interface implementations? Yes.
Should these be permitted in classes too, to specify base interface implementations? Yes. Otherwise the diamond problem isn't solved.
We could actually expand it to classes too, to allow a more distant base implementation to be called. Let's decide that later.
We could actually expand it to work *on* classes too, to allow a more distant base implementation to be called. Let's decide that later.
Question: In an interface, can I say `base(IA)` that isn't a *direct* base interface? Yes.
Question: In a class, can I say `base(IA)` that isn't a *direct* base interface? Yes.
In an interface, can I say `base(IA)` that isn't a *direct* base interface? Yes.
In a class, can I say `base(IA)` that isn't a *direct* base interface? Yes.

View file

@ -161,3 +161,11 @@ Design some remaining 7.1 features
6. Implicitly implementing non-public interface members
7. Not quite implementing a member
8. asynchronous `Main`
## Apr 19, 2017
[C# Language Design Notes for Apr 19, 2017](LDM-2017-04-19.md)
1. Improved best common type
2. Diamonds with classes
3. Structs and default implementations
4. Base invocation