Skip to content

WIP: Validation CE Builder #397

@natalie-o-perret

Description

@natalie-o-perret

Description

Not directly related to #388
But more about the newly released .NET + F# 5.0. The idea would be to provide a dedicated CE builder for validation types supporting the and! keyword.

Sources:

// First, define a 'zip' function
module Result =
    let zip x1 x2 = 
        match x1,x2 with
        | Ok x1res, Ok x2res -> Ok (x1res, x2res)
        | Error e, _ -> Error e
        | _, Error e -> Error e

// Next, define a builder with 'MergeSources' and 'BindReturn'
type ResultBuilder() = 
    member _.MergeSources(t1: Result<'T,'U>, t2: Result<'T1,'U>) = Result.zip t1 t2
    member _.BindReturn(x: Result<'T,'U>, f) = Result.map f x

let result = ResultBuilder()

let run r1 r2 r3 =        
    // And here is our applicative!
    let res1: Result<int, string> =
        result { 
            let! a = r1 
            and! b = r2
            and! c = r3
            return a + b - c 
        }

    match res1 with
    | Ok x -> printfn "%s is: %d" (nameof res1) x
    | Error e -> printfn "%s is: %s" (nameof res1) e

let printApplicatives () =
    let r1 = Ok 2
    let r2 = Ok 3 // Error "fail!"
    let r3 = Ok 4

    run r1 r2 r3
    run r1 (Error "failure!") r3

Repro steps

Please provide the steps required to reproduce the problem

let runValidations v1 v2 v3 =
    let val1 =
        validation {
            let! a = v1
            and! b = v2
            and! c = v3
            return a + b - c
        }

    match val1 with
    | Success s -> printfn "%A is: %A" (nameof val1) s
    | Failure f -> printfn "%A is: %A" (nameof val1) f


let printValidations () =
    let r1 = Validation<string list, int32>.Success 2
    let r2 = Validation<string list, int32>.Success 3
    let r3 = Validation<string list, int32>.Success 4

    runValidations r1 r2 r3
    runValidations
        r1
        (Validation<string list, int32>.Failure [ "Meow 1" ])
        (Validation<string list, int32>.Failure [ "Meow 2" ])

[<EntryPoint>]
let main _ =
    printValidations()
    0
  1. Step B

Expected behavior

validation CE builder should be defined and the output of the program should be:

"val1" is: 1
"val1" is: ["Meow 1"; "Meow 2"]

Actual behavior

The value or constructor 'validation' is not defined.

Known workarounds

open FSharpPlus.Data
module Result =
    let zip r1 r2 =
        match r1, r2 with
        | Ok o1, Ok o2 -> Ok (o1, o2)
        | Error e1, _ -> Error e1
        | _, Error e2 -> Error e2
​
// Next, define a builder with 'MergeSources' and 'BindReturn'
type ResultBuilder() =
    member _.MergeSources(t1: Result<'T,'U>, t2: Result<'T1,'U>) = Result.zip t1 t2
    member _.BindReturn(x: Result<'T,'U>, f) = Result.map f x
​
module Validation =
    let zip x1 x2 =
        match (x1, x2) with
        | Success s1, Success s2 -> Success (s1, s2)
        | Failure f1, Success _  -> Failure f1
        | Success _,  Failure f2 -> Failure f2
        | Failure f1, Failure f2 -> Failure (f1 @ f2)type ValidationBuilder() =
    member _.MergeSources(t1, t2) = Validation.zip t1 t2
    member _.BindReturn(x, f) = Validation.map f x
    member _.Bind(x, f) = Validation.bind f x
​
​
let result = ResultBuilder()
let validation = ValidationBuilder()let runResults r1 r2 r3 =
    // And here is our applicative!
    let res1: Result<int, string> =
        result {
            let! a = r1
            and! b = r2
            and! c = r3
            return a + b - c
        }match res1 with
    | Ok o -> printfn "%A is: %A" (nameof res1) o
    | Error e -> printfn "%A is: %A" (nameof res1) e
​
let runValidations v1 v2 v3 =
    let val1 =
        validation {
            let! a = v1
            and! b = v2
            and! c = v3
            return a + b - c
        }match val1 with
    | Success s -> printfn "%A is: %A" (nameof val1) s
    | Failure f -> printfn "%A is: %A" (nameof val1) f
​
let printResults () =
    let r1 = Ok 2
    let r2 = Ok 3
    let r3 = Ok 4
​
    runResults r1 r2 r3
    runResults r1 (Error "failure!") r3
​
let printValidations () =
    let r1 = Validation<string list, int32>.Success 2
    let r2 = Validation<string list, int32>.Success 3
    let r3 = Validation<string list, int32>.Success 4
​
    runValidations r1 r2 r3
    runValidations
        r1
        (Validation<string list, int32>.Failure [ "Meow 1" ])
        (Validation<string list, int32>.Failure [ "Meow 2" ])[<EntryPoint>]
let main _ =
    printResults()
    printValidations()
    0

Output:

"res1" is: 1
"res1" is: "failure!"
"val1" is: 1
"val1" is: ["Meow 1"; "Meow 2"]

Related information

  • Operating system: Windows 10 professional
  • Branch: master
  • .NET Runtime, CoreCLR or Mono Version: .NET 5.0 + F# 5.0 (lang preview enabled)
  • Performance information, links to performance testing scripts

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions