how to properly use Result.bind

Viewed 194

I am currently trying to implement some validation in my F# program but i'm facing som compile time errors, which I can't solve. Boiled down, i'm trying to do this

let ifFalseThenError x errorMessage = function 
   | true -> Ok x 
   | false -> Error errorMessage
let val1 (initialeSandssynligheder : float[]) =
       initialeSandssynligheder
       |> Seq.sum
       |> (fun x -> x < 2)
       |> (fun x -> ifFalseThenError initialeSandssynligheder "Some error")

let val2 initialeSandssynligheder = 
    initialeSandssynligheder 
    |> Array.exists ((<) 0.0) 
    |> (fun x -> ifFalseThenError initialeSandssynligheder "SomeOtherError")

let beregnAlleOvergangssandsynligheder (initialeSandssynligheder: float[])  : float[] = 
       let initialeSandssynligheder = [|1.0;1.0;1.0;1.0;1.0|] 
       let initssh = initialeSandssynligheder 
                     |> val1
                     |> Result.bind val2  // Error here
       initssh

So this my compiler tells me, that I have a type mismatch. I understand, that both val1 and val2 takes a float[] and returns a Result<Float[], String> and I need to compensate for that, when im piping in my val2 and I though I could do that by using Result.bind. What am i misundertanding.

My error:

Error   FS0001  Type mismatch. Expecting a
    '(bool -> Result<float [],string>) -> 'a'    
but given a
    'Result<'b,'c> -> Result<'d,'c>'    
The type 'bool -> Result<float [],string>' does not match the type 'Result<'a,'b>'... (Path)

So the message is telling my what I though I was compensating for, when adding the Result.bind statement to the last validation

2 Answers

As stated in comments there are multiple compilation errors:

  1. Incorrect numeric data type
  2. Missing arguments to function
  3. Wrong return type hint in beregnAlleOvergangssandsynligheder

Here's a version that resolves these compilation errors with comments describing where changes were made:

let ifFalseThenError x errorMessage =
  function
  | true -> Ok x
  | false -> Error errorMessage

let val1 (initialeSandssynligheder: float array) =
  initialeSandssynligheder
  |> Seq.sum
  |> (fun x -> x < 2.0) // changed to float
  |> (fun x -> ifFalseThenError initialeSandssynligheder "Some error" x) // include x arg

let val2 initialeSandssynligheder =
  initialeSandssynligheder
  |> Array.exists ((<) 0.0)
  |> (fun x -> ifFalseThenError initialeSandssynligheder "SomeOtherError" x) // include x arg

let beregnAlleOvergangssandsynligheder (initialeSandssynligheder: float array) : Result<float array, string> = // Mixing list/array, hint return result
  let initssh =
    initialeSandssynligheder
    |> val1
    |> Result.bind val2 // Error here

  initssh

beregnAlleOvergangssandsynligheder [| 1.0; 1.0; 1.0; 1.0; 1.0 |] // Error because sum > 2
beregnAlleOvergangssandsynligheder [| -1.0 |] // Error because of negative value
beregnAlleOvergangssandsynligheder [| 1.0 |] // Ok

I would also look over the usages of array/list/seq, e.g. you are using array's but I'm not seeing any specific need for that here, maybe seq would be enough? Try to settle on the one you need.

Your intuition on how to use Result.bind is correct. However, there are a number of errors in your code:

  1. The function ifFalseThenError takes a boolean which you're not passing by, in both val1, val2.

  2. In val1 you need to use a float value, e.g. 2.0 rather than int 2.

Here it's corrected

let ifFalseThenError x errorMessage = function 
   | true -> Ok x 
   | false -> Error errorMessage
let val1 (initialeSandssynligheder : float[]) =
       initialeSandssynligheder
       |> Seq.sum
       |> (fun x -> x < 2.0)
       |> (fun x -> ifFalseThenError initialeSandssynligheder "Some error" x)

let val2 initialeSandssynligheder = 
    initialeSandssynligheder 
    |> Array.exists ((<) 0.0) 
    |> (fun x -> ifFalseThenError initialeSandssynligheder "SomeOtherError" x)

let beregnAlleOvergangssandsynligheder (initialeSandssynligheder: float[])  = 
       let initialeSandssynligheder = [|1.0;1.0;1.0;1.0;1.0|] 


       let initssh = initialeSandssynligheder 
                     |> val1 
                     |> Result.bind val2
       initssh

ps. A good way to solve these problems is to add type annotations to all of your functions and parameters.

Related