# C# Language Design Meeting for March 8th, 2023

## Agenda

- [Discriminated Unions](#discriminated-unions)
- [Limited Breaking Changes in C#](#limited-breaking-changes-in-c)

## Quote(s) of the Day

- "It sounds like we're not unified on unions"
- "Anyone feel like we found the weeds?"
- "I shouldn't have used my pizza example"

## Discussion

### Discriminated Unions

https://github.com/dotnet/csharplang/discussions/7010  
https://github.com/dotnet/csharplang/issues/7016

[Last time](LDM-2023-03-01.md#discriminated-unions-summary), we looked at the summary of the discriminated union
working group's investigations. There's a lot there; we also had an additional proposal from a language design member
emeritus, who lead the previous investigation into DUs, on a form of efficient tagged unions. One thing that is
apparent is that, even with the working group's summary, there is a massive design space here. Another point that
becomes obvious quickly is that generics throw many wrenches into any design: you either have a problem where things
like `List<(A | B)>` cannot be freely converted to `List<(A | B | C)>`, or there are inefficiencies in representing
value types that likely preclude union usage in a number of places. Looking to the major .NET language with union
types, F#, they implement tagged unions (though they are considering 
[anonymous type unions](https://github.com/fsharp/fslang-design/blob/0ff87d5ab3160366a96316f739ba31d9a61d0f4a/RFCs/FS-1092-anonymous-type-tagged-unions.md)).

We talked some about how tagged unions implemented via structs is effectively struct inheritance: indeed, it's hard
to conceive of a struct inheritance implementation that would not effectively turn into a tagged discriminated union.
There are some interesting parts to this implementation strategy though: would a union with simple inline values be
able to be more efficiently represented than a union with a nested struct value? For example:

```
union { MyFourInts(int, int, int, int), MyThreeInts(int, int, int) }
union { MyFourInts(StructOfFourInts), MyThreeInts(StructOfThreeInts) }
```

If those have different layouts that make the former more compact than the latter, that could be unfortunate,
particularly if the layout was guaranteed in such a way that we couldn't improve it in the future.

We also thought a bit about how tags will interact with pattern matching. The natural syntax we would want to use
likely conflicts with type patterns: can we make that work? And, in particular, will there be a way to declare a
variable that encapsulates all the state of a particular tag? The tags aren't types, but maybe we can paper over that
for the purposes of pattern matching, using tuples to contain all the state for a particular tag?

We ultimately think there is room for multiple types of unions in C#: a tagged, struct-based variant, and a reference
type variant. However, we aren't ready to make more progress yet; we'll work on determining the next steps for the
working group.

### Limited Breaking Changes in C#

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

Continuing in today's LDM theme of light, easy, non-controversial topics, we took a look at a proposal for making
limited breaking changes in C#. We posted this to csharplang a few days ago, and the response so far has been mostly
positive; plenty of suggestions of how we might tweak it to add more granularity, suggestions to go further (a la
Rust editions), and questions on the cadence. There have only been a few negative reactions to the idea, however,
which is an encouraging sign overall. GitHub can't be our only source of sentiment on this, though, as it's a
self-selecting group; more user research will be needed via other avenues.

We brought up that this might change the way the C# compiler approaches certain problems. Today, the compiler tries
very hard to not change its semantic behavior while binding. When we see a user taking advantage of a language feature
in a newer language version than what they are targeting, we simply produce an error and continue binding. This would
change the compiler to introduce new warnings in _lower_ language versions, and cause the compiler to potentially bind
in different fashions for higher and lower versions. We need to be cognizant of that, and have an idea of how the compiler
change would work before committing to a design that we can't implement.

We also thought about the documentation issue. Today, our documentation is updated for new language versions, so if we
make breaking changes, users on older versions might slowly get left behind our documentation. Inversely, there are many
non-Microsoft sources of documentation (StackOverflow, blogs, tutorial videos, etc.) that never get updated for new
content; users trying to copy and paste or learn from these sources might be hurt. This has already happened with
top-level statements; our tools and docs updated, but many popular 3rd-party intros and tutorials have not. The changes
we're discussing here might not be as much of a concern, however; the cases we've discussed so far are exceptionally
narrow cases of changing how an identifier is interpreted.

A read of the room indicates that we should continue to pursue this topic, so the working group will continue to flesh
out the proposal, gather feedback from user groups, and flesh out the proposal details for future review.
