Search code examples
f#option-type

can I group option checks in F#


I have the following code:

let i  = instrumentFromString arguments.[1]
let o  = podManager.GetExchange().GetLeverageOptions(i.Value)
let l  = parseInt arguments.[2]     |> Option.bind (fun x -> if i.IsSome && o |> List.contains(x) then Some x else None)
let wl = parseDecimal arguments.[3] |> Option.bind (fun x -> if x > 0m && x <= 1m then Some x else None)
let ws = parseDecimal arguments.[4] |> Option.bind (fun x -> if x > 0m && x <= 1m then Some x else None)

it's parsing commands entered into the system.

it's then followed by:

match i.IsSome && l.IsSome && wl.IsSome && ws.IsSome with
| true ->
    ... do stuff
    
| false ->
    stringBuffer {
        if i.IsNone then             $"unkown instrument 'arguments.[1]'\n"
        if l.IsNone && i.IsSome then $"invalid leverage, instrument '{i.Value.Ticker}' supports {podManager.GetExchange().GetLeverageOptions(i.Value)}\n"
        if wl.IsNone then            $"invalid long share, must be > 0 and <= 1\n"
        if ws.IsNone then            $"invalid short share, must be > 0 and <= 1\n"
    }

and I feel like it is a bit convoluted because I parse the arguments, then verify them, then make sure they're all ok and if they're not, I'm going through them one by one again.

so an option would be to transform the parsing lines like this:

let l  = parseInt arguments.[2] |> Option.bind (fun x -> if i.IsSome && o |> List.contains(x) then Result.Ok x else Result.Error "my error message")

so it would group the test and error message, but I still have to check if there is any error, and then parse the possible errors one by one again.

I guess a computation expression where I can accumulate both the ok / error values could work, but there is the issue of the Option / Result conversion as well.

Is there some nice idiomatic way to handle this kind of scenario that I missed?


Solution

  • I recommend you convert your options to results and use the validation builder in FsToolkit.ErrorHandling. This uses F# 5 language features. You can try out this code in an F# script:

    #r "nuget: FsToolkit.ErrorHandling,2.0.0"
    open FsToolkit.ErrorHandling
    
    let a : Result<string, string> = Ok "a"
    let b : Result<string, string> = Error "b missing"
    let c : Result<string, string> = Ok "c"
    let d : Result<string, string> = Error "d missing"
    
    validation {
        let! a = a
        and! b = b
        and! c = c
        and! d = d
        return a, b, c, d
    }
    // Returns: Error ["b missing"; "d missing"]
    

    You should be able to apply this example to your code.