λ0004: The default literal is wrapped by an implicit conversion

A bare default in one branch of an expression that is implicitly converted to Option<T>, Result<T> or Either<L, R> is not the empty monad, it is default(T) wrapped in a Some, an Ok or a Right.

Cause

A default literal is a branch of a conditional (?:), switch or null-coalescing (??) expression, and the whole expression is converted to Option<T>, Result<T> or Either<L, R> through the implicit conversion from the inner type.

Reason for rule

The default literal has no type of its own; it takes the type of the sibling branch. In condition ? 42 : default both branches are int, so the expression is an int and the implicit conversion to Option<int> turns the default branch into Some(0). The code reads as "42 or nothing" but never produces None.

The same happens with Result<T> (an Ok(default) instead of an error) and Either<L, R> (a Right(default) instead of a left). For reference types the compiler reports a nullable warning (CS8604), for value types and tuples it is silent.

A default that is converted directly, without a sibling branch, is fine: Option<int> option = default; is None. A typed default(T) is not reported either, it says what it produces, and neither is a cast such as (int)default outside a branch.

How to fix violations

For an option replace the literal with Option<T>.None, the code fix does this. For a result or an either, construct the error or left value explicitly, e.g. Result<int>.Error(exception) or Either<string, int>.Left("message").

Examples

Disallowed

Option<(string, int)> Lookup(bool condition)
    => condition ? ("hello world", 42) : default; // Some((null, 0)), not None.

Option<int> Parse(int? value) => value ?? default; // Some(0) for null.

Option<string> Name(int id)
    => id switch
    {
        1 => "one",
        _ => default, // Some(null).
    };

Result<int> Compute(bool condition) => condition ? 42 : default; // Ok(0), not an error.

Allowed

Option<(string, int)> Lookup(bool condition)
    => condition ? ("hello world", 42) : Option<(string, int)>.None;

Option<int> Parse(int? value) => Option.FromNullable(value);

Option<string> Name(int id)
    => id switch
    {
        1 => "one",
        _ => Option<string>.None,
    };

Result<int> Compute(bool condition)
    => condition ? 42 : Result<int>.Error(new InvalidOperationException());

// Converted directly, there is no sibling branch: this is None.
Option<int> Empty() => default;