New "Chain" operator for Option
I am looking for an operator for the following use case:
var result = option1.Match(
some => some,
none: option2.Match(
some => some,
none: option3.Match(
some => some,
none: fallbackValue);
I have the following idea, the "Chain" operator:
var result = option1
.Chain(option2)
.Chain(option3)
.Match(
some => some,
none: fallbackValue);
It is somewhat inverse to Bind, in the sense that it does something in case the option is none, instead of in the case the option is some.
Implementation idea:
public static Option<TResult> Chain<TResult>(this Option<TResult> option, Option<TResult> other)
=> option.Match(some => some, none: other);
public static Option<TResult> Chain<TResult>(this Option<TResult> option, Func<Option<TResult>> other)
=> option.Match(some => some, none: other);
I would not use Chain as name because it's too general. E.g. Map and Bind are also some kind of chaining operators. I would suggest OrElse if you want to implement the feature.
We already have the method GetValueOrDefault with:
T? GetValueOrDefault();
T GetValueOrDefault(Func<T> defaultValue);
T GetValueOrDefault(T defaultValue);
so I thought about extending it like:
Option<T> GetValueOrDefault(Func<Option<T>> defaultValue);
Option<T> GetValueOrDefault(Option<T> defaultValue);
but I don't think that's the better solution because the method name suggest getting the boxed value T and not an option.
Another thing I would like to point out is that the Option implements the IEnumerable interface, which means that you can achieve your desired behavior by using Concat like:
var result = option1
.Concat(option2)
.Concat(option3)
.FirstOrDefault(fallbackValue)
The Concat method has a significant disadvantage as it returns an IEnumerable<T> and not an Option<T>. As long as there is no FirstOrNone as IEnumerable extension you always have to do deal with null values like FirstOrDefault().ToOption() if you want an option as result.
Another problem is that it does not work with value types because ToOption is not defined for them unless you use Cast<T?>.
E.g.:
Option<DateTime> result = Option.None<DateTime>().Concat(Option.None<DateTime>()).FirstOrDefault().ToOption(); // does not compile
Option<DateTime> result = Option.None<DateTime>().Concat(Option.None<DateTime>()).FirstOrDefault(); // is 99% a bug
Option<DateTime> result = Option.None<DateTime>().Concat(Option.None<DateTime>()).Cast<DateTime?>().FirstOrDefault().ToOption(); // works but is not intuitive