Add design notes

This commit is contained in:
Mads Torgersen 2017-06-01 17:24:46 -07:00
parent de225ff217
commit ed7d0d86b0
2 changed files with 67 additions and 32 deletions

View file

@ -1,26 +1,39 @@
# C# Language Design Notes for Apr 18, 2017
*Quote of the Day*: "I don't want to require `MainAsync`. It sounds like mayonnaise-ink!"
***Raw notes, yet to be cleaned up - read at your own peril***
## Agenda
1. Default implementations for event accessors in interfaces
2. Reabstraction in a class of default-implemented member
3. `sealed override` with default implementations
4. Use of `sealed` keyword for non-virtual interface members
5. Implementing inaccessible interface members
6. Implicitly implementing non-public interface members
7. Not quite implementing a member
8. asynchronous `Main`
Quote of the Day: "I don't want to require `MainAsync`. It sounds like mayonnaise-ink!"
# DIM Event accessors
# Default implementations for event accessors in interfaces
Today, syntactically, either both or neither accessor can have an implementation.
Should we allow just one to be specified? Overridden?
## Conclusion
If you have only one, you probably have a bug. Let's not allow it. Not blocked for future idiots to ...
If you have only one, you probably have a bug. Let's not allow it for now.
# DIM Reabstraction
# Reabstraction in a class of default-implemented member
Yes, adding a body to an interface member declaration shouldn't break C.
Should an abstract class be allowed to implicitly implement an interface member with an abstract member, even when the interface member has a default implementation?
# DIM Sealed override
## Conclusion
Yes, of course. Adding a body to an interface member declaration shouldn't ever break an implementing class.
# `sealed override` for default implementations
Should it be allowed? would it prevent overrides in *classes* or only in *interfaces*?
@ -30,25 +43,19 @@ It seems odd to prevent either. Also, it is weird in connection with diamond inh
Let's not allowed `sealed` on overrides in interfaces. The only use of `sealed` on interface members is to make them non-virtual in their initial declaration.
# DIM sealed members (not on list)
Some folks in the community find it weird, and that they look too much like things that can be implemented in classes.
# Use of `sealed` keyword for non-virtual interface members
Some folks find it a weird use of `sealed`, and that they look too much like things that can be implemented in classes.
## Conclusion
We think it is going to be useful, but will come back to it. This is a mental model tripping block.
We think non-virtual members in interfaces are going to be useful, but will come back to the syntax. This is a mental model tripping block.
# DIM not quite implementing a member (not int list)
You have a member and implement an interface. The interface adds a new member with a default implementation, that looks like your method but doesn't quite make it an implementation. Bug? Intentional? We can't provide a warning, because it would assume it was a bug.
# Implementing inaccessible interface members
## Conclusion
Can't do anything about this.
# DIM implementing inaccessible interface members (not in list)
The way the runtime works today, a class member can happily implement an interface member that isn't accessible! That's not likely to be dependent on today (no language will generate that interface), but we need to decide what semantics to have here.
The way the runtime works today, a class member can happily implement an interface member that isn't accessible! That's not likely to be depended on today (no language will generate that interface), but we need to decide what semantics to have here.
We could continue to only have *public* members in interfaces be virtual. But if we want protected, internal and private, we should probably have it so they can only be implemented by classes that can see them. But this means that interfaces can *prevent* other assemblies from implementing them! This may be a nice feature - it allows closed sets of implementations.
@ -56,37 +63,53 @@ We could continue to only have *public* members in interfaces be virtual. But if
This is still open, but our current stake in the ground is we should *allow* non-public virtual interface members, but *disallow* overriding or implementing them in places where they are not accessible.
# DIM Implementing a non-public interface member (not in list)
Would we allow them to be implemented implicitly? If so, what is required of the accessibility of the implementing method?:
# Implicitly implementing non-public interface members
Would we allow non-public interface members to be implemented implicitly? If so, what is required of the accessibility of the implementing method? Some options:
* Must be public
* Must be the same accessibility
* Must be the exact same accessibility
* Must be at least as accessible
## Conclusion
For now, let's not allow any of these. We can relax as we think through it.
For now, let's simply not allow it. Only public interface members can be implicitly implemented (and only by public members). We can relax as we think through it.
# Async Main code generation
In the compiler we look for shapes of types. If somebody has a weird implementation of `Task` or `Task<T>`, their `GetAwaiter().GetResult()` returning something else.
# Not quite implementing a member
This is about consuming an awaitable, not producing one. So not just about `Task` and `Task<T>`? After all we just made the language less dependent on those specific types.
You have a member and you implement an interface. The interface adds a new member with a default implementation, that looks like your method but doesn't *quite* make it an implementation. Bug? Intentional? We can't provide a warning, because it would assume it was a bug.
It would not add complexity on the implementation side, it's just about whether we check for those specific types.
## Conclusion
On the other hand, if we don't allow it, folks can just have an `async Task Main` that awaits it.
Can't do anything about this.
One problem is that current awaitable things aren't necessarily built to block on `GetResult()`. It may not work right.
# asynchronous `Main`
We've decided to allow a `Main` method that returns `Task` and `Task<int>`. Whether it's `async` or not is completely optional, and an implementation detail of the method.
This feature relies on the pattern of calling `GetAwaiter().GetResult()` on the returned task, and on an expectation that this call blocks until the task is complete. That is the case for the framework's implementations of `Task` and `Task<T>`.
What if someone uses an alternative implementation? We can't prevent that. They either know what they are doing, or they are asking for it.
What about other awaitable types? Should they be allowed? This would be easy enough to implement; the concern is whether it is reasonable to expect their `GetResult()` method to block? After all, the compiler has not previously relied on this in its use of `GetResult()` on awaitable types, so one would assume that no particular effort has been put into ensuring it in the implementation of those types.
If we don't allow other awaitables in `Main` directly, folks can easily work around it just by having an `async Task Main` that awaits it:
``` c#
static MyTask MyMain() { ... }
static async Task Main() => await MyMain(); // problem solved
```
## Conclusion
Let's still keep the `async` keyword optional.
Let's stick with `Main` instead of `MainAsync` for now.
Let's stick with the name `Main` instead of `MainAsync` for now.
Let's refine the rule:
Let's stick to `Task` and `Task<T>`, but refine the rule:
1. Look for `Main()` or `Main(string[] args)`
2. Does exactly one return `void` or `int`? If one, use that. If more, error.

View file

@ -149,3 +149,15 @@ Design some remaining 7.1 features
[C# Language Design Notes for Apr 11, 2017](LDM-2017-04-11.md)
1. Runtime behavior of ambiguous default implementation
## Apr 18, 2017
[C# Language Design Notes for Apr 18, 2017](LDM-2017-04-18.md)
1. Default implementations for event accessors in interfaces
2. Reabstraction in a class of default-implemented member
3. `sealed override` with default implementations
4. Use of `sealed` keyword for non-virtual interface members
5. Implementing inaccessible interface members
6. Implicitly implementing non-public interface members
7. Not quite implementing a member
8. asynchronous `Main`