csharplang/meetings/2020/LDM-2020-10-07.md

104 lines
6.1 KiB
Markdown
Raw Normal View History

2020-10-08 00:57:37 +02:00
# C# Language Design Meeting for October 7th, 2020
## Agenda
1. [`record struct` syntax](#record-struct-syntax)
2. [`data` members redux](#data-members-redux)
2020-10-08 01:01:51 +02:00
3. [`ReadOnlySpan<char>` patterns](#readonlyspanchar-patterns)
2020-10-08 00:57:37 +02:00
## Quote of the Day
- "And we're almost there (famous last words, I'll knock on something)"
- "What about `record delegate`... does `record interface` make sense... I'll go away now"
## Discussion
### `record struct` syntax
First, we looked at what syntax we would use to specify `struct` types that are records. There are two possibilities:
```cs
record struct Person;
struct record Person;
```
In the former, `record` is the modifier on a `struct` type. In the latter, `struct` is the modifier on `record` types.
We also considered whether to allow `record class`.
#### Conclusion
By unanimous agreement, `record struct` is the preferred syntax, and `record class` is allowed. `record class` is
synonymous with just `record`.
### `data` members redux
With `data`, we come back to the same question we had at the end of [Monday](LDM-2020-10-05.md#data-members): should we have
`data` members, and if we do, what should they mean. `data` can be viewed as a strategy to try and get nominal records in a
single line, much like positional records. This is not a goal of brevity just for brevity's sake: in discriminated unions,
listing many variants as nominal records is a design goal, and single-line declarations are particularly useful for this case.
Many languages with discriminated unions based on inheritance introduce short class-declaration syntax for use in union
declarations, and that was a defining goal for where `record` types would be useful.
Another area worth examining is the original proposal for the `data` keyword. In the original proposal, primary constructors
would have meant the same thing in `record` types as well non-`record` types, and `data` would have been used as a modifier
on the primary constructor parameter to make it a public get/init property. `data` applied to a member, then, would have been
a natural extension and reuse of that keyword. With the original scenario gone, there are a few concrete scenarios we think
are highly related to, and will influence, the `data` keyword:
1. Use as a single-line in a discriminated union. While this is motivating, it's worth considering that, at least to some LDT
members, anything more than 2 properties as `data` members doesn't look great, and would perhaps work better as a multi-line
construct, which will line up visually. This seems a reasonable concern, so `data` may not be the solution we're looking for.
2. Use in required properties, as it seems likely that we will need some keyword to indicate that a property is a required
member in a type. While `data` could be orthogonal to some other required property keyword, it's likely there will be at least
some interaction as we'd likely want `data` to additionally imply required. It's also possible that we could have just a single
keyword to mark a property required, and it gives you what we believe are the useful defaults for such a property.
3. Use in general primary constructors as a way to say "give me the same thing a `record` would have for this parameter". This
would be somewhat resurrecting the original use case for the keyword, as we could then retcon `record` primary constructors
as implying `data` on all parameters for you, but you can then opt-in to them in regular primary constructors as well. `data`
members in a class, then, would again become a natural reuse and extension of the keyword on a primary constructor parameter.
#### Conclusion
We'll leave `data` out of the language for now. Our remaining motivating scenarios are things that will be worked on more in
C# 10 cycle, and absent clearer designs in those spaces the need and design factors for `data` are too abstract. Once we work
more on these 3 scenarios, we'll revisit `data` and see if a need for it has emerged.
### `ReadOnlySpan<char>` patterns
https://github.com/dotnet/csharplang/issues/1881
We have a community PR to implement the Any Time feature of allowing constant strings to be used to pattern match against
a `ReadOnlySpan<char>`. This would be acknowledging special behavior for `ReadOnlySpan<char>` with respect to constant strings,
but we already have acknowledged special behavior for `Span` and `ReadOnlySpan` in the language, around `foreach`. We also
considered whether this could be a breaking change, but we determined it could not be one: `ReadOnlySpan` cannot be converted
to `object` or to an interface as it is a ref struct, so if there exists a pattern today operating on one today the input type
of that pattern match must be `ReadOnlySpan<char>`. There were two open questions:
#### Should we allow `Span<char>`
The question is if `Span<char>` should also be allowed as well as `ReadOnlySpan<char>`. All of the same arguments about
compat apply to `Span<char>`. We also considered whether `Memory`/`ReadOnlyMemory` should be allowed inputs. Unlike `Span`/`ReadOnlySpan`,
though, there are backcompat concerns with `Memory` that cannot be overlooked, as they are not ref structs. It is very
easy to obtain a `Span`/`ReadOnlySpan` from a `Memory`/`ReadOnlyMemory`, however, so the need isn't as great.
##### Conclusion
`Span<char>` is allowed. `Memory<char>`/`ReadOnlyMemory<char>` are not.
#### Is this specific to `switch`, or can it be any pattern context
The original proposal here just mentioned `switch`. However, the implementation allows it to be used in any pattern context,
such as `is`.
##### Conclusion
Allowed in all pattern contexts.
#### Final Notes
We also discussed making sure that switching on a `Span`/`ReadOnlySpan` is as efficient for large switch statements as switching
on a string is. Over 6 cases, the compiler will take the hashcode of the string and use it to implement a jump table to reduce the
number of string comparisons necessary. This method is added to the `PrivateImplementationDetails` class in an assembly, and we
should make sure to do the same thing for `Span<char>` and `ReadOnlySpan<char>` here as well so the cost to using one isn't higher
than allocations would be from just doing a substring and matching with that.