WIP: Applicative support - #75
Conversation
|
@TheAngryByrd this program runs without an annotation in the CE: // Learn more about F# at http://fsharp.org
open System
type Validation<'a,'err> = Result<'a, 'err list>
[<RequireQualifiedAccess>]
module Validation =
let ok x : Validation<_,_> = Ok x
let error e : Validation<_,_> = List.singleton e |> Error
let ofResult x =
Result.mapError List.singleton x
let apply f x =
match f, x with
| Ok f, Ok x -> Ok (f x)
| Error errs, Ok _ | Ok _, Error errs -> Error errs
| Error errs1, Error errs2 -> Error (errs1 @ errs2)
let retn x = ofResult (Ok x)
let map2 f x y =
apply (apply (retn f) x) y
let map3 f x y z =
apply (map2 f x y) z
let bind (f : 'a -> Validation<'b, 'err>) (x : Validation<'a,'err>) : Validation<_,_>=
Result.bind f x
let zip x1 x2 : Validation<_,_> =
match x1,x2 with
| Ok x1res, Ok x2res -> Ok (x1res, x2res)
| Error e, Ok _ -> Error e
| Ok _, Error e -> Error e
| Error e1, Error e2 -> Error (e1 @ e2)
[<AutoOpen>]
module ValidationCE =
type ValidationBuilder() =
member __.Return (value: 'T) =
Validation.ok value
member _.BindReturn(x: Validation<'T,'U>, f) : Validation<_,_> = Result.map f x
member __.Bind
(result: Validation<'T, 'TError>, binder: 'T -> Validation<'U, 'TError>)
: Validation<'U, 'TError> =
Validation.bind binder result
member _.MergeSources(t1: Validation<'T,'U>, t2: Validation<'T1,'U>) : Validation<_,_> = Validation.zip t1 t2
let validation = ValidationBuilder()
[<AutoOpen>]
module ValidationCEExtensions =
// Having Choice<_> members as extensions gives them lower priority in
// overload resolution and allows skipping more type annotations.
type ValidationBuilder with
member __.Bind
(result: Result<'T, 'TError>, binder: 'T -> Validation<'U, 'TError>)
: Validation<'U, 'TError> =
result
|> Validation.ofResult
|> Validation.bind binder
member _.BindReturn(x: Result<'T,'U>, f) : Validation<_,_> = x |> Validation.ofResult |> Result.map f
member _.MergeSources(t1: Validation<'T,'U>, t2: Result<'T1,'U>) : Validation<_,_> = Validation.zip t1 (Result.mapError List.singleton t2)
member _.MergeSources(t1: Result<'T,'U>, t2: Validation<'T1,'U>) : Validation<_,_> = Validation.zip (Result.mapError List.singleton t1) t2
member _.MergeSources(t1: Result<'T,'U>, t2: Result<'T1,'U>) : Validation<_,_> = Validation.zip (Result.mapError List.singleton t1) (Result.mapError List.singleton t2)
let f () =
let expected = Error ["Error 1"; "Error 2"]
let actual = validation {
let! a = Ok 3
and! b = Ok 2
and! c = Error "Error 1"
and! d = Error "Error 2"
return a + b - c - d
}
printfn "%A %A" expected actual
()
[<EntryPoint>]
let main argv =
f()
0 // return an integer exit codeHowever I have also noticed that in some cases a type annotation is needed. Thanks for testing this out though, @dsyme is looking for more folks to give it a shot |
|
Ok I can also get it to compile just fine now. Please call an exorcist. |
|
Thanks, @TheAngryByrd for taking this up. Due to some personal reasons, I am not in a position to actively work on this library. I am checking the email notifications periodically, and I will jump in if required. Thanks again :) |
|
@demystifyfp No worries! I use this extensively at work so I’m fine with maintaining it fully. Would you be willing to add me as an owner of the nuget package so I can push whenever PRs get merged? |
|
@cartermp I am once again asking for your support. I hope summoning you scares the code into behaving again. I'm trying to add overloads for testCaseAsync "Happy Path AsyncResult" <| async {
let! actual = asyncResult {
let! a = AsyncResult.retn 3
and! b = AsyncResult.retn 2
and! c = AsyncResult.retn 1
return a + b - c
}
Expect.equal actual (Ok 4) "Should be ok"
}
testCaseAsync "Happy Path Async" <| async {
let! actual = asyncResult {
let! a = Async.singleton 3
and! b = Async.singleton 2
and! c = Async.singleton 1
return a + b - c
}
Expect.equal actual (Ok 4) "Should be ok"
}This is the compiler error I end up getting for the second test: We are using the hack of using extension members which has a lower priority when binding, so maybe this just isn't possible. You can pull the latest at d9e960e which has this issue. You can't use FAKE to build until fsprojects/FAKE#2499 is merged, so just |
|
Thanks, @TheAngryByrd. I have added you as an owner of the packages in NuGet. |
|
Given the amount of work to do for AsyncValidation/TaskValidation/JobValidation and the lack of use cases around those currently, I'm opting to not implementing them for now. |
|
I'm having issues with travis: dotnet/core#4542 (comment) I'm going to accept this PR since I don't want to fight with it. I'll open a separate issue to get it fixed. |
- Adds Applicative Support for FSharp 5.0. Credits [Jimmy Byrd](https://github.com/TheAngryByrd) - (#75) - Reduces required FSharp.Core version to 4.3.4. Credits [Jimmy Byrd](https://github.com/TheAngryByrd) - (#80)
- Adds Applicative Support for FSharp 5.0. Credits [Jimmy Byrd](https://github.com/TheAngryByrd) - (#75) - Reduces required FSharp.Core version to 4.3.4. Credits [Jimmy Byrd](https://github.com/TheAngryByrd) - (#80)
This adds support for the new applicative in F# 5.0.
The
resultCE will remain mostly the same. It will still return aResult<'a,'err>.and!only gives users some performance optimization.The new
validationCE will be a much bigger difference. It's return type will beResult<'a, 'err list>. When given the example:using
and!in this case will run each operation parallel resulting in a list of'errto be given. Currently you do have to use some type annotations to tell the compiler how to handle the overload resolution. @cartermp, is there something I can do about my implementation so consumers don't have to specify that type annotation?CE's to finish:
AsyncValidation CETaskValidation CEJobValidation CE