fslang-suggestions icon indicating copy to clipboard operation
fslang-suggestions copied to clipboard

Result module regularity with option/voption

Open T-Gro opened this issue 7 months ago • 4 comments

I propose we extend the Result module to add APIs which exist for option/voption in FSharp.Core already.

The Result type's dual nature (Ok/Error) makes some Option APIs less applicable

The missing APIs that would improve Result ergonomics:

  1. orElse
  2. orElseWith
  3. toSeq - since toList and toArray is there already

API Comparison: Result vs Option/ValueOption

APIs Missing from Result but Present in Option/ValueOption

API Option ValueOption Result Should Add? Reasoning
orElse Makes sense - would return first Ok or fallback Result
orElseWith Makes sense - lazy evaluation for fallback Result
get Unsafe - Result should encourage proper error handling
toSeq Convert to sequence
ofNullable Would need to specify what Error value to use for null
toNullable Feels similar to get, also symmetrical ofNullable (above) missing anyway

The existing way of approaching this problem in F# is to write it yourself.

Pros and Cons

The advantages of making this adjustment to F# are easier access for beginners.

The disadvantages of making this adjustment to F# are cost of more code to maintain and ship

Extra information

Estimated cost (XS, S, M, L, XL, XXL): S

Affidavit (please submit!)

Please tick these items by placing a cross in the box:

  • [x] This is not a question (e.g. like one you might ask on StackOverflow) and I have searched StackOverflow for discussions of this issue
  • [x] This is a language change and not purely a tooling change (e.g. compiler bug, editor support, warning/error messages, new warning, non-breaking optimisation) belonging to the compiler and tooling repository
  • [x] This is not something which has obviously "already been decided" in previous versions of F#. If you're questioning a fundamental design decision that has obviously already been taken (e.g. "Make F# untyped") then please don't submit it
  • [x] I have searched both open and closed suggestions on this site and believe this is not a duplicate

Please tick all that apply:

  • [x] This is not a breaking change to the F# language design
  • [x] I or my company would be willing to help implement and/or test this

For Readers

If you would like to see this issue implemented, please click the :+1: emoji on this issue. These counts are used to generally order the suggestions by engagement.

T-Gro avatar May 22 '25 12:05 T-Gro

I'd like to zoom in on orElseWith. We should have the same semantics as we did with defaultWith in https://github.com/fsharp/fslang-suggestions/issues/1123#issuecomment-1179170318 which is to pass the 'error in rather than unit for the error path function. unit makes sense in the case of v/option types since None doesn't contain any information where as Result does.

For a more concrete example, here's the implementation from FsToolkit.ErrorHandling

    let inline orElseWith
        ([<InlineIfLambda>] ifErrorFunc: 'error -> Result<'ok, 'errorOutput>)
        (result: Result<'ok, 'error>)
        : Result<'ok, 'errorOutput> =
        match result with
        | Ok x -> Ok x
        | Error e -> ifErrorFunc e

TheAngryByrd avatar Jun 04 '25 13:06 TheAngryByrd

Also, if we add ofNullable (and decide what to do for null), then surely we should also add ofOption with similar semantics.

There's also the ofObj / toObj pair.

Tarmil avatar Jun 04 '25 14:06 Tarmil

Also, if we add ofNullable (and decide what to do for null), then surely we should also add ofOption with similar semantics.

There's also the ofObj / toObj pair.

FSharp.Core could provide a marker type for the TError value in case of incoming nulls/None/ValueNone. But it would not compose well and I am afraid it would defeat the purpose of using Result (unless it is immediately fed into mapError)

Having the error value as an user-provided argument seems like a better choice:

type MyDomainErrors = FileNotFound | InputEmpty | ...
let x : string | null = null
let r = x |> Result.ofNullable InputEmpty 

T-Gro avatar Jun 05 '25 07:06 T-Gro

Having the error value as an user-provided argument seems like a better choice

Yeah, that's how I usually implement it in custom utils modules.

It also corresponds to FsToolkit.ErrorHandling's requireSome.

Tarmil avatar Jun 05 '25 08:06 Tarmil