Skip to content

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

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 Unions using a new Type #1524

Closed
mcintyre321 opened this issue May 15, 2018 · 2 comments
Closed

Proposal: Add Unions using a new Type #1524

mcintyre321 opened this issue May 15, 2018 · 2 comments

Comments

@mcintyre321
Copy link

mcintyre321 commented May 15, 2018

Background

Although there are various improvements for pattern matching/switch improvements, there doesn't seem to be a proposal for adding Discriminated Union types (like those in f# / TypeScript) which provide exhaustive matching over a closed set of Types. In addition, the types in a OneOf union can be from any assembly, and don't require source access. e.g. you can have OneOf<string, int, MyClass, SomeoneElsesClass>

It would be extremely useful to have this in c#, as it helps prevent a whole class of bugs (missed conditions/catches) and removes the need for certain types of boilerplate (e.g. return objects) and bad patterns (e.g. exceptions as control flow)

Problem

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#?

Solution

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, OneOf, which adds a OneOf<T0, ..., TN> type (although I've seen other proposals refer to an Option<T0, ..., TN> type) which has .Match(Func<T0, ..., TOut>, ..., Func<T0, ..., TOut> methods. By using implicit operators to create the OneOf instances from values, the syntax is very terse and ledgible.

The OneOf<T0, .., TN> type also provides .Switch and .TryGetTX(out TX value, out TRemainder remainer) methods.

Example of using a OneOf as a return value:

public OneOf<User, InvalidName, NameTaken> CreateUser(string username)
{
    if (!IsValid(username)) return new InvalidName();
    var user = _repo.FindByUsername(username);
    if(user != null) return new NameTaken();
    var user = new User(username);
    _repo.Save(user);
    return user;
}

example of Matching

    OneOf<string, ColorName, Color> backgroundColor = "Red"; //uses implicit casts to reduce overhead
    Color c = backgroundColor.Match(
        str => CssHelper.GetColorFromString(str),
        name => new Color(name),
        col => col
   );
    

As new types are added to the OneOf definition, compiler errors are generated wherever the union is Matched or Switched, as the methods are required to have the correct number of lambda parameters.

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.

This can be included in the BCL without language changes, although maybe Union would be a better name than OneOf (depends who the langauge is aimed at).

There could be some language change too though, e.g. instead of public OneOf<Foo, Bar> Baz(), we could have public (Foo|Bar) Baz(), or some improvements to matching. E.g. @jnm2 posted some ideas here.


this proposal was originally made at dotnet/roslyn#14208

@ufcpp
Copy link

ufcpp commented May 15, 2018

here doesn't seem to be a proposal for adding Discriminated Union types

dup of championed #113

@mcintyre321
Copy link
Author

@ufcpp I'll close this and add the content as comment to there

@mcintyre321 mcintyre321 changed the title Proposal: Add Discriminated Unions using a new Type Proposal: Add Unions using a new Type Aug 5, 2021
@mcintyre321 mcintyre321 reopened this Aug 5, 2021

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants