You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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.
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:
example of Matching
As new types are added to the OneOf definition, compiler errors are generated wherever the union is
Match
ed orSwitch
ed, 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 thanOneOf
(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 havepublic (Foo|Bar) Baz()
, or some improvements to matching. E.g. @jnm2 posted some ideas here.this proposal was originally made at dotnet/roslyn#14208
The text was updated successfully, but these errors were encountered: