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

6.2 KiB

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.

(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:

[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?

    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

    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

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.

    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

switch ((a, b))
{

It has been proposed that we permit

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.