> Usually you combine this with pattern matching and other functional features and the whole thing makes it convenient in the end. That part is missing in C#
You mean like this?

    string foo = result.MatchFirst(
        value => value,
        firstError => firstError.Description);
Or this?

    ErrorOr<string> foo = result
        .Then(val => val * 2)
        .Then(val => $"The result is {val}");
Or this?

    ErrorOr<string> foo = await result
        .ThenDoAsync(val => Task.Delay(val))
        .ThenDo(val => Console.WriteLine($"Finsihed waiting {val} seconds."))
        .ThenDoAsync(val => Task.FromResult(val * 2))
        .ThenDo(val => $"The result is {val}");
With pattern matching like this?

    var holidays = new DateTime[] {...};
    var output = new Appointment(
        DayOfWeek.Friday, 
        new DateTime(2021, 09, 10, 22, 15, 0), 
        false
    ) switch
    {
        { SocialRate: true } => 5,
        { Day: DayOfWeek.Sunday } => 25,
        Appointment a when holidays.Contains(a.Time) => 25,
        { Day: DayOfWeek.Saturday } => 20,
        { Day: DayOfWeek.Friday, Time.Hour: > 12 } => 20,
        { Time.Hour: < 8 or >= 18 } => 15,
        _ => 10,
    };
C# pattern matching is pretty damn good[0] (seems you are not aware?).

[0] https://timdeschryver.dev/blog/pattern-matching-examples-in-...

None of your examples use native C# pattern matching. And without language support like e.g. discriminated unions you can't have exhaustive pattern matching in C#. So you'll have to silence the warnings about the missing default case or always add one, which is annoying.

I mean, it's not a stretch to see how you can use native pattern matching with ErrorOr result types.

    #!/usr/local/share/dotnet/dotnet run
    #:package ErrorOr@2.0.1
    using ErrorOr;

    var computeRiskFactor = ErrorOr<decimal> ()
        => 0.5m; // Just an example

    var applyAdjustments = ErrorOr<decimal> (decimal baseRiskFactor)
        => baseRiskFactor + 0.1m; // Just an example

    var approvalDecision = computeRiskFactor()
        .Then(applyAdjustments)
        .Match(
            riskFactor => riskFactor switch {
                < 0.5m => "Approved",
                < 0.75m and >= 0.5m => "Approved with Conditions",
                >= 0.75m and < 0.9m => "Manual Review",
                _ => "Declined"
            },
            errors => "Error computing risk factor"
        );

    Console.WriteLine($"Loan application: {approvalDecision}");
(Fully contained program, BTW)

Here's the OCaml version:

    let compute_risk_factor () = 0.5

    let apply_adjustments base_risk_factor = base_risk_factor +. 0.1

    let approval_decision =
      let risk_factor = compute_risk_factor () |> apply_adjustments in
      match risk_factor with
      | r when r < 0.5 -> "Approved"
      | r when r < 0.75 -> "Approved with Conditions"
      | r when r < 0.9 -> "Manual Review"
      | _ -> "Declined"

    let () =
      print_endline approval_decision

Still not functional enough?...Or you just don't like C#? No point moving goal posts.