Add LDM notes for Nov. 14, 2018
This commit is contained in:
parent
050eba840f
commit
4811c98606
131
meetings/2018/LDM-2018-11-14.md
Normal file
131
meetings/2018/LDM-2018-11-14.md
Normal file
|
@ -0,0 +1,131 @@
|
|||
|
||||
# C# Language Design Notes for Nov 14, 2018
|
||||
|
||||
## Agenda
|
||||
|
||||
1. Base call syntax for default interface implementations
|
||||
2. Switch exhaustiveness and null
|
||||
|
||||
## Discussion
|
||||
|
||||
### Default interface implementations
|
||||
|
||||
Example of base calls:
|
||||
|
||||
```C#
|
||||
interface I1 { void M(); }
|
||||
interface I2 { void M(); }
|
||||
interface I3 : I1, I2 { void I1.M() { } void I2.M() { } }
|
||||
interface I4 : I1, I2 { void I1.M() { } void I2.M() { } }
|
||||
interface I5 : I3, I4
|
||||
{
|
||||
void I1.M()
|
||||
{
|
||||
base<I3>(I1).M();
|
||||
base<I4>(I1).M();
|
||||
}
|
||||
void I2.M()
|
||||
{
|
||||
base<I3>(I2).M();
|
||||
base<I4>(I2).M();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Q: Should we even allow the programmer to call explicit implementations?
|
||||
|
||||
In existing C# code this is not allowed since all explicit implementations
|
||||
are private. You can always call by casting to the interface if the interface
|
||||
is not re-implemented, but there's no way to do this via a base call.
|
||||
|
||||
However, there's no way to use overriding default interface implementation in
|
||||
C# 8.0 *except* for explicit implementation, so not allowing it would basically
|
||||
not allow the scenario.
|
||||
|
||||
Java does allow this scenario, so full compat would require us to add the feature.
|
||||
|
||||
Q: What type of dispatch would we use in the runtime?
|
||||
|
||||
One way is to constrain to the interface, then do a virtual dispatch for a
|
||||
particular method. This probably would require additional (fairly expensive)
|
||||
runtime work.
|
||||
|
||||
Q: What does the syntax need to express?
|
||||
|
||||
One problem is for `I3`, where by signature `M` is ambiguous because it's not
|
||||
clear whether you mean `I1.M` and `I2.M`. The syntax would need to specify both
|
||||
that the `I3` implementation is requested *and* whether or not to choose `I1.M` or
|
||||
`I2.M`.
|
||||
|
||||
One other option is to just not provide a way of disambiguating `M`. If there's an
|
||||
ambiguity you can't call any particular `M`.
|
||||
|
||||
**Conclusion**
|
||||
|
||||
Let's do the simplified form. If the `M` being called is ambiguous, it's illegal to
|
||||
call such a method. The only configuration we're considering is which base to look
|
||||
for the method.
|
||||
|
||||
#### Syntax
|
||||
|
||||
```C#
|
||||
namespace N {
|
||||
interface I1<T> { int M(string s) => s.Length; }
|
||||
}
|
||||
class C : I1<T>
|
||||
{
|
||||
int I1.M(string s)
|
||||
{
|
||||
// Options:
|
||||
base<N.I1<T>>.M(s);
|
||||
base(N.I1<T>).M(s);
|
||||
N.I1<T>.base.M(2);
|
||||
base{N.I1<T>}
|
||||
base:N.I1<T>.M(s);
|
||||
base!N.I1<T>.M(s);
|
||||
base@N.I1<T>.M(s);
|
||||
base::global::N.I1<T>.M(s);
|
||||
base.(N.I1<T>).M(s);
|
||||
(base as N.I1).M(s); // base isn't an expression, so this isn't legal today
|
||||
((N.I1)base).M(s); // same here
|
||||
base.N.I1.M(s); // clash with a member on base, doesn't allow for qualified name
|
||||
N.I1.M(s); // this is currently a static call on I1, can't work
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
We think the forms that take advantage of `base` not being an expression are
|
||||
too clever and would just be confusing. `N.I1.base.M(2)` is possibly tricky
|
||||
for human readability since `base` is in the middle and is harder to notice
|
||||
without coloration.
|
||||
|
||||
One problem with `base(N.I1).M(s)` is if we want to add "invocation" operators
|
||||
to the language, that would disallow invoking your base.
|
||||
|
||||
`base::N.I1<T>.M(s)` has some history in the language.
|
||||
|
||||
**Conclusion**
|
||||
|
||||
Decided on `base(N.I1<T>).M(s)`, conceding that if we have an invocation
|
||||
binding there may be problem here later on.
|
||||
|
||||
|
||||
### Switch exhaustiveness and null
|
||||
|
||||
Current design: warning about not handling all inputs *except* null.
|
||||
|
||||
Q: What happens when the switch actually doesn't match what was given at
|
||||
compile time?
|
||||
|
||||
**Proposal**:
|
||||
|
||||
A new `MatchFailureException` that extends `ArgumentException`. This allows
|
||||
the compiler to use an existing exception for down-level support. There's
|
||||
no data, just a message.
|
||||
|
||||
**Conclusion**
|
||||
|
||||
The base should be `InvalidOperationException`, not `ArgumentException`. If
|
||||
the argument being switched on can be trivially boxed into object, we'll add
|
||||
it as an argument to the `MatchFailureException`. Otherwise, just fill in
|
||||
`null`.
|
|
@ -212,12 +212,14 @@ Discussion of records proposals:
|
|||
1. Where can #nullable go?
|
||||
2. Open issues with pattern matching
|
||||
|
||||
# Upcoming meetings
|
||||
|
||||
## Nov 14, 2018
|
||||
|
||||
- Base call syntax for default interface implementations (Aleksey, David, Michal)
|
||||
- Runtime exception for switch expressions (Neal)
|
||||
[C# Language Design Notes for Nov 14, 2018](LDM-2018-11-14.md)
|
||||
|
||||
1. Base call syntax for default interface implementations
|
||||
2. Switch exhaustiveness and null
|
||||
|
||||
# Upcoming meetings
|
||||
|
||||
## Nov 28, 2018
|
||||
|
||||
|
|
Loading…
Reference in a new issue