csharplang/meetings/2020/LDM-2020-12-16.md
2020-12-17 16:51:11 -08:00

6.4 KiB

C# Language Design Meeting for December 14th, 2020

Agenda

  1. List patterns
  2. Definite assignment changes

Quote of the Day

  • "They are syntax forms you little devil. No amount pedanticism is too much in C#."

Discussion

List patterns

https://github.com/dotnet/csharplang/pull/3245

Coming out of Monday's meeting, we had a few different competing proposals for syntax. As a quick recap:

  1. The original proposal as is.
  2. Put the brackets from 1 inside the braces on the top level.
  3. Use braces for the list pattern, with the empty case being:
    1. No empty case.
    2. {,}
    3. Add a [pattern] form that allows testing and potentially extracting the length of a collection.
    4. Add a new combinator to make the braces explicitly a list pattern, which would allow { } to be the base case.

After the notes were published, we took the list and had an email discussion to narrow in on the specifics of each of these cases. Cases 2, 3.i, 3.ii, and 3.iv were not defended in this email chain, and coming into today's meeting there were 4 different main syntax proposals, the final 3 being variations of 3.iii from the original list (in psuedo-antlr):

  1. The original proposal. This introduces a new pattern, which uses '[' (pattern (',' pattern)* ']') as the syntax of that new pattern. This cannot be expressed as a top-level concept in a positional_pattern or a property_pattern because the braces can be ambiguous with the type component of these patterns.
  2. type? ('(' subpatterns? ')')? ('[' pattern ']')? ('{' property_subpatterns_or_list_element_patterns '}')?.
    This form modifies the positional_pattern syntax introduced in C# 8 to add a length pattern section, defined by the middle [pattern] section, and modifies the final braces to contain either a set of positional subpatterns, or a set of list element patterns, but not both. To test both property elements and list elements, a conjunctive pattern needs to be used.
  3. type? ('(' subpatterns? ')')? ('[' pattern ']')? ('{' property_subpatterns '}')? ('{' list_subpatterns '}')?.
    This is very similar to 2, except that it allows both property and list subpatterns at the same time.
  4. type? ('(' subpatterns? ')')? ('[' pattern ']')? ('{' property_subpatterns_and_list_element_patterns '}')?.
    This is very similar to 2, except that it allows both property subpatterns and list subpatterns in the same set of braces. Consider subproposals of this version to require properties first, list elements first, or no ordering requirements.

The very important goal for the language team here is to follow the correspondence principle. That means that if you construct using one syntax construct, you should use the same construct to deconstruct. For collection types, this means that we strongly want to prefer using curly braces as the deconstruction syntax, rather than square brackets, because collection initializers use the braces. It is possible that at some point in the future, we could add a collection literal syntax that uses square brackets, but there is strong history in C# to avoid using the brackets in this fashion. Up to this point, the brackets have always contained indexes or lengths in the language, and lists of things to initialize have always been inside braces. Changing that at this point, even if we later seek to add conformance by introducing a new collection literal, would be asking C# users to unlearn a concept that has been unchanged since C# 1.0, which is very concerning to us. Given this desire, option 1 deviates too much from existing C#, and we will instead focus on one of the latter options.

Of these latter options, option 2 can be viewed as a strict subset of both 3 and 4, as either will allow using conjunctive patterns to separate out the list and property patterns if users feel that the combination is unreadable. Additionally, we again turn to the correspondence principle: today, you cannot combine both collection initializers and object initializers. By the correspondence principle, then, you should not be able to combine them in the same pattern during deconstruction. We're not necessarily opposed to allowing collection and object initializers to be combined in the future, but that is out of scope for the collection pattern changes.

Finally, in discussions Monday and over email, we also took a brief look at indexer patterns as possible property_subpatterns. These would look something like this: { [1]: pattern, [2..^4]: var slice }. This form seems like a good next step after list patterns to allow deconstructioning objects with indexers. These indexer arguments could allow non-integer constants as well as multiple arguments, giving a deconstruction mechanism for dictionaries that corresponds to object initializers in this area.

Conclusion

We'll move forward with the general syntax proposed in option 2, with a length subpattern and allowing either property subpatterns or list subpatterns in the same "recursive pattern" section. We still need to translate the psuedo-spec above into a formal specification. We also did not address the open questions around whether the length pattern should be applicable to types of IEnumerable, and if so whether [0] is the only allowed pattern or if any pattern is allowable.

Definite assignment changes

https://github.com/dotnet/csharplang/discussions/4240

This is an area of longstanding pain for C# users: any time conditional evaluation and comparison to constants mix, definite assignment cannot figure out what is going on and variables that the user can see are obviously assigned are not considered assigned. We're highly in support of this idea in general, as everyone has run into this at some point or another in their C# careers. The definite assignment rules are written in a very syntax-driven form, and thus this proposal is written in a very syntax-driven form to update the relevant constructs. Despite that, we do wonder whether we can make these rules more holistic and systematic, such that we don't need to make them syntax-specific like they need to be today. We're also less enthused about the conditional expression version. If it fell out of more general rules it would be nice, but it's not highly important like the null conditional and coalescing changes seem to be.

Conclusion

The general idea is approved. We'll work to see if we can generalize the rules a bit, and submit a proposal for review on github.