λ1007: Prefer SelectMany over Match

A Match that propagates the error state unchanged and maps the success value to a new monad should be expressed as SelectMany.

Cause

Match is called on an Option<T>, Either<L, R> or Result<T>, the result type is the same monad type as the receiver, and the error branch is the monad's error constructor (Option<T>.None, Either<L, R>.Left, Result<T>.Error).

Reason for rule

Passing the error state through and binding a function over the success value is the monadic bind operation, SelectMany. Spelling it out with Match obscures the fact that this is an ordinary chaining step and prevents the use of LINQ query syntax.

How to fix violations

Replace the Match call with SelectMany, passing the former some/right/ok argument. A code fix is available.

Examples

Disallowed

static Option<int> Example(Option<int> option)
    => option.Match(none: Option<int>.None, some: x => Option.Return(x + 10));

static Result<int> Example(Result<int> result)
    => result.Match(error: Result<int>.Error, ok: x => Result.Return(x * 2));

Allowed

static Option<int> Example(Option<int> option)
    => option.SelectMany(x => Option.Return(x + 10));

static Result<int> Example(Result<int> result)
    => result.SelectMany(x => Result.Return(x * 2));

// The result type differs from the receiver, so this is a legitimate Match.
static Option<string> Example(Option<int> option)
    => option.Match(none: Option<string>.None, some: x => x.ToString());