163 lines
6.2 KiB
Markdown
163 lines
6.2 KiB
Markdown
# C# Language Design Notes for Apr 4, 2018
|
|
|
|
***Warning: These are raw notes, and still need to be cleaned up. Read at your own peril!***
|
|
|
|
## Agenda
|
|
|
|
# Design review follow-up
|
|
|
|
## Ranges
|
|
|
|
Let's flip what we prototype to implement the `Index` and `^` option.
|
|
|
|
``` c#
|
|
(Index) ^ i
|
|
```
|
|
|
|
## Nullable
|
|
|
|
Let's start having the discussion on external annotation soon, bringing in the right people from BCL's etc.
|
|
|
|
One proposal for external annotations:
|
|
|
|
``` c#
|
|
[Nullable(typeof(MyClass))]
|
|
class C
|
|
{
|
|
static object f1;
|
|
static object? f2;
|
|
}
|
|
```
|
|
|
|
Nice because it's source based.
|
|
|
|
May also use or mirror JetBrains' annotations. https://github.com/JetBrains/ExternalAnnotations
|
|
|
|
Offer the capability just for BCL vs for general libraries?
|
|
|
|
|
|
# Ordering of ref and partial keywords
|
|
|
|
There's a strict ordering around `ref` and `partial` preceding `struct`. We need to relax a bit, or a lot:
|
|
|
|
1. `ref partial` and `partial ref` are both allowed, but need to be right before `struct`
|
|
2. allow `ref` and `partial` anywhere in the modifier list
|
|
|
|
Let's do 2, since that leads to a more consistent language. No reason to have special limitation on some modifiers, even though there might be guidance (and code style enforcement in the IDE) to put things in a certain order.
|
|
|
|
# Patterns
|
|
|
|
In Roslyn, we would probably put deconstructors that only return the "important" data, whereas non-optional tokens would be left out. (We're essentially reinventing abstract syntax tree.) If we evolve the node, we can add another overload of the deconstructor with more out parameters.
|
|
|
|
The code gen is now roughly as efficient as what you would have written, though it doesn't know about testing `Kind` fields, of course (which actually isn't always more efficient).
|
|
|
|
## Order of evaluation in pattern-matching
|
|
|
|
Giving the compiler flexibility in reordering the operations executed during pattern-matching can permit flexibility that can be used to improve the efficiency of pattern-matching. The (unenforced) requirement would be that properties accessed in a pattern, and the Deconstruct methods, are required to be "pure" (side-effect free, idempotent, etc). That doesn't mean that we would add purity as a language concept, only that we would allow the compiler flexibility in reordering operations.
|
|
|
|
It may be a little disconcerting not to specify order of evaluation, but if patterns don't do what user code will do, they will be slower and will not be generally adopted/useful.
|
|
|
|
People generally don't care what the spec says - they care if something changes release over release. So it's more important that the compiler doesn't change what it generates from version to version.
|
|
|
|
### Conclusion
|
|
|
|
We're good with this. We don't want to slow down pattern matching just to guarantee order of evaluation when people do something side effecting where they shouldn't.
|
|
|
|
We'll make an effort to discover if changes to the compiler change the output of existing code. Then we'll have a discussion about whether that is warranted.
|
|
|
|
## Range Pattern
|
|
|
|
If we have a range operator `1..10`, would we similarly have a range pattern? How would it work?
|
|
|
|
``` c#
|
|
if (ch is in 'a' to 'z')
|
|
switch (ch) {
|
|
case in 'a' to 'z':
|
|
```
|
|
|
|
### Conclusion
|
|
|
|
We like the idea, but are unsure about value yet, and probably want to pursue it in connection with `foreach` and `from` uses.
|
|
|
|
|
|
## `var` deconstruct pattern
|
|
|
|
It would be nice if there were a way to pattern-match a tuple (or Deconstructible) into a set of variables declared only by their designator, e.g. the last line in this match expression
|
|
|
|
``` c#
|
|
var newState = (GetState(), action, hasKey) switch {
|
|
(DoorState.Closed, Action.Open, _) => DoorState.Opened,
|
|
(DoorState.Opened, Action.Close, _) => DoorState.Closed,
|
|
(DoorState.Closed, Action.Lock, true) => DoorState.Locked,
|
|
(DoorState.Locked, Action.Unlock, true) => DoorState.Closed,
|
|
var (state, _, _) => state };
|
|
```
|
|
|
|
(Perhaps not the best example since it only declares one thing on the last line)
|
|
|
|
This would be based on some grammar like this
|
|
|
|
``` antlr
|
|
var_pattern
|
|
: 'var' variable_designation
|
|
;
|
|
```
|
|
|
|
where the _variable_designation_ could be a _parenthesized_variable_designation_, i.e. generalizing the current construct.
|
|
|
|
To make this syntactically unambiguous, we would no longer allow `var` to bind to a user-declared type in a pattern. Forbidding it from binding to a constant would also simplify things, but probably isn't strictly necessary.
|
|
|
|
At a recent LDM it was suggested that `var` could perhaps be used as a placeholder for an unknown type taken from context. But that would conflict with this usage because this usage changes the syntax of what is permitted between the parens (designators vs patterns).
|
|
|
|
This is implemented in the current prototype, in which `var` is now a contextual keyword.
|
|
|
|
### Conclusion
|
|
|
|
This is a useful shorthand, and we already love it in C# 7.0 deconstructing declarations. We should have it here too, for symmetry and brevity.
|
|
|
|
|
|
## ref/lvalue-producing pattern switch expression
|
|
|
|
As currently designed, the “switch expression” yields an rvalue.
|
|
|
|
``` c#
|
|
e switch { p1 when c1 => v1, p2 when c2 => v2 }
|
|
```
|
|
@agocke pointed out that it might be valuable for there to be a variant that produces a ref or an lvalue.
|
|
|
|
1. Should we pursue this?
|
|
2. What would the syntax be?
|
|
`e switch { p1 when c1 => ref v1, p2 when c2 => ref v2 }`
|
|
|
|
You could argue that while the conditional (ternary) operator is often used in perf-sensitive code, and combining with `ref` was important there, it's less likely to see a combination of patterns and high-performance code.
|
|
|
|
### Conclusion
|
|
|
|
This feels right, but is not all that important. Let's keep it on the back burner.
|
|
|
|
## switching on a tuple literal
|
|
|
|
In order to switch on a tuple literal, you have to write what appear to be redundant parens
|
|
|
|
``` c#
|
|
switch ((a, b))
|
|
{
|
|
```
|
|
|
|
It has been proposed that we permit
|
|
|
|
``` c#
|
|
switch (a, b)
|
|
{
|
|
```
|
|
|
|
There are a couple of ways of doing it:
|
|
|
|
1. Make the outer parens optional when there is a tuple literal
|
|
2. Make the inner parens optional when there is a tuple literal
|
|
3. It's a list that's part of the switch statement (and expression, when we get there)
|
|
|
|
## Conclusion
|
|
|
|
Let's do 1. This means that in certain cases switch statements will no longer have parentheses, so analyzers that expect that will have to adjust.
|
|
|