meetings/working-groups/discriminated-unions/DU-2022-11-07.md
As a reminder, we use this syntax for describing unions forms.
Pickup up from last week, we talked about equivalency again. This time, we started by focusing on the differences between union (A | B) (an anonymous union of types) and
union NamedAOrB (A | B) (a named union of types). We do generally think that union (A | B) and union (B | A) should at least be implicitly convertible, if not identical,
and that has implications on the grow up story. Much like with tuples, we don't think anonymous union types belong in public APIs, except in cases like Zip where the
implementation detail of the tuple is the purpose of the API (in other words, when the likelihood of adding or removing a new element is nonexistent). Tuples that need to
be in public API generally want to grow up to be positional records, and we might need something similar here for untagged named unions. In thinking about it more, though,
we're not certain of the need for exporting this type of union. It would have limitations, such as the inability to have two different cases with the same type (a string result
or string error, for example, couldn't be represented without a separate tag), so it's possible that the roles proposal is enough for us here. Something like
role NamedAOrB : (A | B); would give a name to the case, give us equivalency with the underlying type, and maintain discouragement of putting untagged unions in public
type hierarchies.
We considered a few different ways that users might want to pattern match against anonymous unions of types:
void M((int|string) x)
{
object o = x;
_ = o switch
{
(int|string) => 0,
_ => 0
};
_ = o switch
{
int or string => 1,
_ => 1
};
_ = o switch
{
int => 2,
string => 2,
_ => 2,
};
// Switch on x
_ = x switch
{
int => 2, // Could use x.IsTypeOf<int>
string => 2,
_ => 2,
};
}
There are a few ways this could work:
IsType<T> method.We don't have any decisions yet, we're still exploring the space. We'll keep exploring further.