91 lines
5.2 KiB
Markdown
91 lines
5.2 KiB
Markdown
# C# Language Design Meeting for May 26th, 2021
|
|
|
|
## Agenda
|
|
|
|
1. [Open questions in list patterns](#open-questions-in-list-patterns)
|
|
|
|
## Quote of the Day
|
|
|
|
- "If somebody lays down on the track I'm perfectly willing to be taken hostage"
|
|
|
|
## Discussion
|
|
|
|
### Open questions in list patterns
|
|
|
|
https://github.com/dotnet/csharplang/blob/main/proposals/list-patterns.md
|
|
|
|
Today we looked at a number of open questions in list patterns, currently being implemented.
|
|
|
|
#### Patterns allowed after a slice operator
|
|
|
|
We first revisited the question of what to allow after a `..` pattern. Previously we stated that any possible pattern
|
|
should be allowable in this position; however, there is some concern that this could be confusing to code readers. We
|
|
forsee the vast majority of patterns in this location being simply capturing the slice into a pattern, and there is a
|
|
dissimilarity here with other containing pattern contexts. For positional, property, list, and length patterns, nested
|
|
patterns are all visually inside delimiters (`()`, `{}`, and `[]`, respectively). Here, the pattern would be on the
|
|
slice element, but _not_ visually inside. There is also some concern that perhaps we should take the position that using
|
|
a nested pattern here is just generally not good form: we think that such syntax will become quickly unreadable, going
|
|
against the general goal of the feature. There's also potential for being visually hard to parse: something like
|
|
`..var x and not null` parses very differently in a pattern (as `..(var x and not null)`) than it does in regular syntax,
|
|
for something like `..1+2` (which parses as `(..1)+2`).
|
|
|
|
Looking at these concerns gives us 3 general options for how to move forward:
|
|
|
|
1. Simplify the syntax, and only allow `..identifier`. Users can omit the type entirely. General patterns are not allowed.
|
|
2. Only allow `..var pattern`. This has symmetry with other declaration patterns.
|
|
3. Allow all patterns.
|
|
|
|
Option 1 is initially somewhat attractive because it will simplify the 99% case here. However, we have some concerns: it's
|
|
not regular with other declarations, and some users do not like implicitly-typed variables. It also makes it harder to
|
|
change our minds here in the future, if we ever wanted to add an `all` or `any` pattern. Meanwhile, version 2 suggests
|
|
a pattern generality that would not exist, and (to the user that runs into this) for seemingly arbitrary reasons.
|
|
|
|
Given our concerns with the first two approaches, we think that, despite earlier concerns, approach 3 is the way to go.
|
|
We can take a stronger stance in the IDE and our documentation with fixers and best practices to help ensure that nested
|
|
patterns don't get too crazy here.
|
|
|
|
##### Conclusion
|
|
|
|
Original design upheld, any pattern is allowed after a slice.
|
|
|
|
#### Multi-dimensional array support
|
|
|
|
Currently, the list pattern specification has an open question as to whether MD-arrays should be supported. One of our
|
|
goals for this feature was to bring symmetry between object creation and deconstruction/pattern forms, and array/collection
|
|
initializers work with MD-arrays today, suggesting they should be supported in patterns. However, our current rules depend
|
|
on types being indexable/countable/sliceable, which MD-arrays are not today. They're also generally not a huge area of
|
|
investment for either the language or the runtime, so it feels odd to make sure they work here but not also with the rest of
|
|
the indexing/counting/slicing features.
|
|
|
|
##### Conclusion
|
|
|
|
Not supported. If we want to make a general MD-array focused release, we would want to revisit all the areas they're currently
|
|
lacking, not just list patterns.
|
|
|
|
#### Binding rules for Slice
|
|
|
|
This is a double-sided question on how we bind `Slice` methods: should we allow default parameters, and should we allow
|
|
extension methods for Slice? In general, we think we should follow the same rules as the existing support around slicing,
|
|
which is no on both questions. We can look at a general feature to loosen the restrictions as a separate proposal.
|
|
|
|
##### Conclusion
|
|
|
|
No default parameters or extension methods.
|
|
|
|
#### Tests emitted for `{ .. }`
|
|
|
|
The main question here is whether `{ .. }` should emit a check that `Length` or `Count` is greater than or equal to 0, or if it should
|
|
simply be a no-op. This must be specified in the language, as not doing so would result in
|
|
`default(ImmutableArray<int>) is { .. }` having undefined behavior (it will throw or not, depending on whether `Length` is
|
|
evaluated). We think that `..` without a trailing pattern and without any other element patterns is similar in concept to a
|
|
`_` pattern: `default(ImmutableArray<int>) is { Length: _ }` does not actually evaluate `Length`, and thus won't throw.
|
|
The `..` is the same: _other_ things cause `Length` to be evaluated, such as the precense of other element patterns or a
|
|
length pattern. A slice pattern can change the nature of that check (moving it from an `==` to a `>=`), but it doesn't add
|
|
the check on its own. Similarly, `.._` will not call slice, just like other discard operations. This does mean that, for
|
|
list implementations that do not follow framework design guidelines and return negative lengths or counts, `{ .. }` will not
|
|
fail when they do so, but we think that is fine.
|
|
|
|
##### Conclusion
|
|
|
|
`{ .. }` will not emit a `Length` check, and `{ .._ }` will not call `Slice`.
|