Some of my previous goals for results types included
- Low-barrier to creating result types
- Alias result types to mitigate long generic type parameters
- Interoperability between result types, being able to map from one to another.
Basic Result Type
It’s pretty quick and easy to create a result type with a union approximation
This could then be consumed like
The main issue here is pattern exhaustiveness (the
_ case). There is no way to limit the recognized derivatives to just Success and Error. Therefore we must always handle the open-ended case when pattern matching or surpress errors. This breaks my mental model of what I expect when matching on a result type. We can hide that detail by providing some
.Handle(onSuccess, onFailure), but then we loose the desired benefit of pattern matching over callbacks.
Note that we can limit the actual possible derivatives. The
Result<TSuccess, TError> constructor can be made
internal to limit derivatives to the source assembly. We can even make the constructor to
private and still derive nested types, like how Success and Error are nested above.
It should be possible to improve the static analyzer to recognize limited derivatives based on constructor accessibility, but the current analyzer does not do so.
Aliasing / Deriving
C# does not allow type aliasing, so any named results have to be derived types of Result.
Deriving from our earlier result type leads to some issues. Mainly,
Result<int,string> and not valid instances of the derived type
This could be worked around in the class-based result-type experiment. However, a similar workaround doesn’t compile for the union-like approach. The success and failure types would need to inherit from arbitrary derivatives of Result, but generic type parameters cannot be inherited from.
Overall, making a result type using union-like records is pretty quick and easy. Unfortunately, pattern matching is a bit awkward and scenario-specific aliases cannot be derived from the root result type.