Skip to content
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

New "Chain" operator for Option #31

Open
JMarianczuk opened this issue Sep 12, 2023 · 2 comments
Open

New "Chain" operator for Option #31

JMarianczuk opened this issue Sep 12, 2023 · 2 comments

Comments

@JMarianczuk
Copy link
Contributor

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.

@JMarianczuk
Copy link
Contributor Author

JMarianczuk commented Sep 12, 2023

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);

@Tyrrx
Copy link
Member

Tyrrx commented Sep 12, 2023

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. 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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants