-
Notifications
You must be signed in to change notification settings - Fork 4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Proposal: Add Discriminated Unions using a new Type #14208
Comments
Possible dupe of #728, depending on how attached you are to giving them names. I'd love to see support for this in C#. A "proper" implementation would also allow an Enum-like unions have also been mentioned in the various pattern matching, completeness, and record enum types threads. |
As per what was discussed in #188, one idea was that union types would be implemented like: public abstract sealed class AOrB
{
class A();
class B();
} Which would result in two types, So all we all have to do is endless harange @gafter et al to get straight on to fully implementing unions/ADTs, records and patetrn matching right after C# 7 ships (and, ideally, releasing these features as a C# 7.1 along with an update to the next release of VS 😆) |
That's clever, but means that a type could only be part of a single union. You, also wouldn't be able to form unions with types from external libraries. Ultimately, using inheritance is just working around the CLR type system which hasn't really evolved since generics were added. That said, I imagine it's something we'd have to live with, 'cause the odds of a CLR change seem to be near zero. |
That's possible with ADTs in F#, type Union =
| A of TypeA
| B of TypeB And perhaps, in C# (#6739), enum class Union {
A(TypeA),
B(TypeB),
} Both TypeA and TypeB could be declared in external assemblies. If you don't want to specify the exact subtype in the construction (A and B), I think an implicit conversion will help. |
@lorcanmooney, true, that would be a limitation. The actual proposal allows for more than my example though. A public abstract sealed struct Maybe<T>
{
struct None();
struct Some<T>(T value);
} but then it would not be possible to assign an public class Either<T1, T2>
{
public static IEnumerable<Either<T1, T2>> AsEither(IEnumerable<T1> leftEnumeration) =>
leftEnumeration.Select(Either<T1, T2>.Left);
public static IEnumerable<Either<T1, T2>> AsEither(IEnumerable<T2> rightEnumeration) =>
rightEnumeration.Select(Either<T1, T2>.Right);
public static Either<T1, T2> Left(T1 item) => new Either<T1, T2>(item);
public static Either<T1, T2> Right(T2 item) => new Either<T1, T2>(item);
}
...
IEnumerable<Either<A,B>> aOrBList = (some IEnumerable<A>).AsEither<A,B>(); Another possibility might be that the "extension everything" proposals for C# 7+1 might allow extension operators, which in turn might allow something like: public static implicit operator IEnumerable<AOrB>(this IEnumerable<A> list) =>
list.Select(a => new AOrB.A(a)); |
It is not most important question regarding discriminated unions, but... |
@alrz @gordanr @DavidArno You can achieve all this already without syntax/CLR changes via my proposal, and against types from external libraries.
or you can do them on the fly without declaring a type (anonymous unions?)
*in my library, |
@lorcanmooney I've updated the title and description to reinforce that this particular proposal is not asking for language/CLR extensions. |
@mcintyre321, There are many 3rd party libraries around that offer unions, such as my own Succinc<T>. The equivalent to your code would be: var backgroundColor = new Union<string, ColorName, Color>("Red");
var c = backgroundColor.Match<Color>()
.CaseOf<string>().Do(str => CssHelper.GetColorFromString(str))
.CaseOf<ColorName>().Do(name => new Color(name))
.CaseOf<Color>().Do(col => col)
.Result(); which is more verbose than the way you have done it, but that's because I offer pattern matching features, such as "where guards", rather than just type matching. But what I offer is just an interim until the language catches up. With source generators, it'll be possible to create ADT's with proper labels for use in patterns etc, but again that's not as good as having it all baked into the language. |
@MadsTorgersen What are your thoughts on this? I appreciate it's not a 'first-class' way to get unions into C#, but the syntax for declaring them seems very idiomatic. |
Discussion should continue at dotnet/csharplang#485 |
Although c# 7 contains various features for pattern matching, disappointingly there doesn't seem to be a proposal for adding Discriminated Union types, with exhaustive matching.
Adding this at the language level raises a lot of questions, e.g. how do you define a closed class hierarchy? Can it be done as succinctly as in F#?
The feature can be added fairly simply using a custom type, in the same way
Tuple<T0, ..., TN>
was added. It's not as pretty as in TypeScript, but it works:I maintain a library which adds a
OneOf<T0, ..., TN>
type (although I've seen other proposals refer to anOption<T0, ..., TN>
type) which has.Match<TOut>(Func<T0, ..., TOut>, ..., Func<T0, ..., TOut>
methods.If you've used DUs you will know that using these as return types e.g.
public OneOf<User, InvalidName, NameTaken> CreateUser(string username)
is really powerful as you can extend the types on the return type, and get a compiler warning in areas of the code which don't handle it, something you don't get with a switch or non-exhaustive pattern match.edit: This is not a proposal to extend the language or change the CLR, merely to include a custom Type.
The text was updated successfully, but these errors were encountered: