If you're not coming from a strongly typed functional language, it's still a pattern you're not used to. Which might be a bit of a roundabout way to say that I agree about your last part, developers without contact to that kind of language will struggle at first with a pattern like this.

I know how to use this pattern, but the C# version still feels weird and cumbersome. 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#. And I think it makes a different in understanding, as you would usually build ony our experience with pattern matching to understand how to handle this case of Result|Error.

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