csharplang/meetings/2018/LDM-2018-04-30.md

202 lines
5 KiB
Markdown
Raw Normal View History

2018-05-01 22:15:49 +02:00
# C# Language Design Notes for Apr 30, 2018
2018-05-19 01:04:30 +02:00
***Warning: These are raw notes, and still need to be cleaned up. Read at your own peril!***
2018-05-01 22:15:49 +02:00
# Switch expressions
Current syntax:
``` c#
e switch
{
1 => "one",
2 => "two",
var x when x > 2 => "too many",
_ => "too few"
}
```
2018-05-19 01:04:30 +02:00
No `default` - instead use `_`.
2018-05-01 22:15:49 +02:00
If the compiler thinks there are cases you don't handle, it'll warn. If you actually don't handle a case we throw an exception (NRE for prototype, something else in the long run).
Also discussed:
- use `match` instead of `switch`
- keyword first, followed by parens, like `switch` statement
- or without parens
- optional implicit default at the end?
The current thing is nice for error recovery.
The lack of statements inside may be a frustration, but that's orthogonal. Let's leave it for now.
## Exploration
2018-05-19 01:04:30 +02:00
Is there a way to instead generalize the syntax from the conditional (ternary) operator? After all, semantically speaking this could be viewed as a general form: Conditional operators and switch expressions on bool are semantically equivalent.
2018-05-01 22:15:49 +02:00
``` c#
e
? true => e1
: false => e2
2018-05-19 01:04:30 +02:00
```
The fact that you know the number of colons today means you can have fewer parentheses than you would get away with here.
``` c#
e ?
true => e1 ? x : y :
false => e2
```
It's not in fact obvious whether there should be many `?`s and one `:`, or one `?` and many `:`s:
``` c#
2018-05-01 22:15:49 +02:00
// Interpret ? as following the tested expression, and : as a separator of test/result pairs
e
? 1 => "one"
: 2 => "two"
: var x when x > 2 => "too many"
: _ => "too few"
// Interpret ? as introducing test/result pairs, and : as introducing the fallback result
e
? 1 => "one"
? 2 => "two"
? var x when x > 2 => "too many"
: "too few"
2018-05-19 01:04:30 +02:00
```
2018-05-01 22:15:49 +02:00
2018-05-19 01:04:30 +02:00
The `=>` glyph is probably not right in a `?:` style syntax. It would have to be something else that more clearly signals pattern/result pairs.
2018-05-01 22:15:49 +02:00
2018-05-19 01:04:30 +02:00
``` c#
2018-05-01 22:15:49 +02:00
e
? 1 -> "one"
? 2 -> "two"
? var x when x > 2 -> "too many"
: "too few"
2018-05-19 01:04:30 +02:00
```
2018-05-01 22:15:49 +02:00
2018-05-19 01:04:30 +02:00
## Considering our options
``` c#
2018-05-01 22:15:49 +02:00
// Compromise - terser
e ? { 1: "one", 2: "two", var x when x > 2: "two many", "too few" }
// Formatted
e ?
{
1: "one",
2: "two",
var x when x > 2: "two many",
"too few"
}
e switch
{
1: "one",
2: "two",
var x when x > 2: "two many",
"too few"
}
// Some of these in context of a var
var x = e
? 1 -> "one"
: 2 -> "two"
: var x when x > 2 -> "too many"
: _ -> "too few";
var x = e ?
{
1: "one",
2: "two",
var x when x > 2: "two many",
"too few"
};
var x = e switch
{
1: "one",
2: "two",
var x when x > 2: "two many",
"too few"
};
// Some of these as one-liners in a method call
M(x switch { null => 0, _ => x.Length }); // 1
M(x switch { null: 0, x.Length }); // 2
M(x ? null -> 0 : _ -> x.Length); // 3
M(x ? { null: 0, x.Length }); // 4
M(x ? null -> 0 : x.Length); // 5
M(x ? { null -> 0, x.Length }); // 6
```
Argument against 1:
``` c#
strings.Select(x => x switch { null => 0, _ => x.Length }); // Lots of => with different meaning
```
Argument against `->`: Has meaning in unsafe code
2018-05-19 01:04:30 +02:00
Argument against `:` as used in 4: Clashes with other uses of `:`.
2018-05-01 22:15:49 +02:00
Where input is an expression rather than a variable:
2018-05-19 01:04:30 +02:00
2018-05-01 22:15:49 +02:00
``` c#
M(e switch { null => 0, var x => x.Length }); // 1 - 0
M(e switch { null: 0, var x: x.Length }); // 2 - 13 - 6
M(e ? null -> 0 : var x -> x.Length); // 3 - 1
M(e ? { null: 0, var x: x.Length }); // 4 - 6 - 3
M(e ? { null -> 0, var x -> x.Length }); // 6 - 0
```
We might want to allow the last thing to be a default value without pattern, but not in the prototype.
2018-05-19 01:04:30 +02:00
## Conclusion
2018-05-01 22:15:49 +02:00
2018-05-19 01:04:30 +02:00
The prototype will have version 2. We're saving for later whether the last clause should be able to leave off a pattern.
2018-05-01 22:15:49 +02:00
# Property pattern
``` c#
if (e is { Name: "Mads", Employer: { ID: string id } }) { WriteLine(id); } // 1 - Current
if (e is { Name = "Mads", Employer = { ID = string id } }) { WriteLine(id); } // 2
if (e is { Name == "Mads", Employer == { ID == string id } }) { WriteLine(id); } // 3
if (e is { Name is "Mads", Employer is { ID is string id } }) { WriteLine(id); } // 4
```
2018-05-19 01:04:30 +02:00
1 is what we have implemented, but it clashes a little with what we just decided for switch expressions.
2018-05-01 22:15:49 +02:00
2 mirrors object initializers the most
3 implies equality, but clashes in meaning with `==` elsewhere
4 emphasizes `is` as a means for applying patterns
``` c#
var result = person switch
{
{ Name: "Mads", Employer: { ID: string id } }: id,
(_, id: var pid){ Name: "Matt" }: pid,
_: null
};
```
There's a clash but maybe it doesn't feel too bad. The second pattern with three different meanings of `:` is certainly not common.
``` c#
var result = person switch
{
{ Name is "Mads", Employer is { ID is string id } }: id,
(_, var pid){ Name is "Matt" }: pid,
_: null
};
```
2018-05-19 01:04:30 +02:00
## Conclusion
2018-05-01 22:15:49 +02:00
Decision: stay with `:` for prototype, remains open question though!