Skip to content

Commit

Permalink
Adds AsyncOption, TaskOption, JobOption helpers
Browse files Browse the repository at this point in the history
* TaskOption
Working on #77 -> AsyncOption.map

* TaskOption
Wirking on #77 -> AsyncOption.Bind

* TaskOption
Working on #77 -> AsyncOption.apply

* TaskOption
Working on #77 -> AsyncOption.retn

* TaskOption
Working on #77 -> AsyncOption basic Operators

* TaskOption
Working on #77 -> TaskOption basic Functions

* TaskOption
Fixes #77 -> JobOption

* TaskOption
Fixes #77 -> Add AsyncOption Tests to execution
  • Loading branch information
Micha-kun authored Jun 5, 2020
1 parent 40c152a commit 224955f
Show file tree
Hide file tree
Showing 19 changed files with 495 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,10 @@
<Compile Include="Job.fs" />
<Compile Include="JobResult.fs" />
<Compile Include="JobResultCE.fs" />
<Compile Include="JobOptionCE.fs" />
<Compile Include="JobResultOp.fs" />
<Compile Include="JobOption.fs" />
<Compile Include="JobOptionCE.fs" />
<Compile Include="JobOptionOp.fs" />
<Compile Include="List.fs" />
<Compile Include="JobResultOption.fs" />
<Compile Include="JobResultOptionCE.fs" />
Expand Down
26 changes: 26 additions & 0 deletions src/FsToolkit.ErrorHandling.JobResult/JobOption.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
namespace FsToolkit.ErrorHandling

open Hopac
open Hopac.Infixes

[<RequireQualifiedAccess>]
module JobOption =

let inline map f ar =
Job.map (Option.map f) ar

let bind f (ar: Job<_>) = job {
let! opt = ar
let t =
match opt with
| Some x -> f x
| None -> job { return None }
return! t
}

let retn x =
job { return Some x }

let apply f x =
bind (fun f' ->
bind (fun x' -> retn (f' x')) x) f
10 changes: 10 additions & 0 deletions src/FsToolkit.ErrorHandling.JobResult/JobOptionOp.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
namespace FsToolkit.ErrorHandling.Operator.JobOption

open FsToolkit.ErrorHandling

[<AutoOpen>]
module JobOption =

let inline (<!>) f x = JobOption.map f x
let inline (<*>) f x = JobOption.apply f x
let inline (>>=) x f = JobOption.bind f x
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@
<Compile Include="TaskResultCE.fs" />
<Compile Include="TaskResultOp.fs" />
<Compile Include="List.fs" />
<Compile Include="TaskOption.fs" />
<Compile Include="TaskOptionCE.fs" />
<Compile Include="TaskOptionOp.fs" />
<Compile Include="TaskResultOption.fs" />
<Compile Include="TaskResultOptionCE.fs" />
<Compile Include="TaskResultOptionOp.fs" />
Expand Down
27 changes: 27 additions & 0 deletions src/FsToolkit.ErrorHandling.TaskResult/TaskOption.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
namespace FsToolkit.ErrorHandling

open System.Threading.Tasks
open FSharp.Control.Tasks.V2.ContextInsensitive

[<RequireQualifiedAccess>]
module TaskOption =

let inline map f ar =
Task.map (Option.map f) ar

let bind f (ar: Task<_>) =
task {
let! opt = ar
let t =
match opt with
| Some x -> f x
| None -> task { return None }
return! t
}

let retn x =
task { return Some x }

let apply f x =
bind (fun f' ->
bind (fun x' -> retn (f' x')) x) f
10 changes: 10 additions & 0 deletions src/FsToolkit.ErrorHandling.TaskResult/TaskOptionOp.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
namespace FsToolkit.ErrorHandling.Operator.TaskOption

open FsToolkit.ErrorHandling

[<AutoOpen>]
module TaskOption =

let inline (<!>) f x = TaskOption.map f x
let inline (<*>) f x = TaskOption.apply f x
let inline (>>=) x f = TaskOption.bind f x
25 changes: 25 additions & 0 deletions src/FsToolkit.ErrorHandling/AsyncOption.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
namespace FsToolkit.ErrorHandling

open System.Threading.Tasks

[<RequireQualifiedAccess>]
module AsyncOption =

let inline map f ar =
Async.map (Option.map f) ar

let bind f ar = async {
let! opt = ar
let t =
match opt with
| Some x -> f x
| None -> async { return None }
return! t
}

let retn x =
async { return Some x }

let apply f x =
bind (fun f' ->
bind (fun x' -> retn (f' x')) x) f
10 changes: 10 additions & 0 deletions src/FsToolkit.ErrorHandling/AsyncOptionOp.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
namespace FsToolkit.ErrorHandling.Operator.AsyncOption

open FsToolkit.ErrorHandling

[<AutoOpen>]
module AsyncOption =

let inline (<!>) f x = AsyncOption.map f x
let inline (<*>) f x = AsyncOption.apply f x
let inline (>>=) x f = AsyncOption.bind f x
2 changes: 2 additions & 0 deletions src/FsToolkit.ErrorHandling/FsToolkit.ErrorHandling.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,9 @@
<Compile Include="ValidationCE.fs" />
<Compile Include="Option.fs" />
<Compile Include="OptionCE.fs" />
<Compile Include="AsyncOption.fs" />
<Compile Include="AsyncOptionCE.fs" />
<Compile Include="AsyncOptionOp.fs" />
<Compile Include="List.fs" />
<None Include="Script.fsx" />
</ItemGroup>
Expand Down
21 changes: 21 additions & 0 deletions tests/FsToolkit.ErrorHandling.JobResult.Tests/Expect.JobOption.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
namespace Expects.JobOption

module Expect =
open Expecto
open Hopac

let hasJobValue v jobX =
let x = run jobX
if v = x then
()
else Tests.failtestf "Expected %A, was %A." v x


let hasJobSomeValue v jobX =
let x = run jobX
TestHelpers.Expect.hasSomeValue v x


let hasJobNoneValue jobX =
let x = run jobX
TestHelpers.Expect.hasNoneValue x
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,11 @@
<Compile Include="../FsToolkit.ErrorHandling.Tests/SampleDomain.fs" />
<Compile Include="../FsToolkit.ErrorHandling.Tests/TestData.fs" />
<Compile Include="../FsToolkit.ErrorHandling.Tests/Expect.fs" />
<Compile Include="Expect.JobOption.fs" />
<Compile Include="Expect.JobResult.fs" />
<Compile Include="Result.fs" />
<Compile Include="JobResult.fs" />
<Compile Include="JobOption.fs" />
<Compile Include="JobOptionCE.fs" />
<Compile Include="JobResultCE.fs" />
<Compile Include="List.fs" />
Expand Down
100 changes: 100 additions & 0 deletions tests/FsToolkit.ErrorHandling.JobResult.Tests/JobOption.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
module JobOptionTests


open Expecto
open Expects.JobOption
open SampleDomain
open TestData
open TestHelpers
open FsToolkit.ErrorHandling
open FsToolkit.ErrorHandling.Operator.JobOption
open System
open Hopac

let runJobSync = run
let createPostSome = createPostSome >> Job.fromAsync
let getFollowersSome = getFollowersSome >> Job.fromAsync
let allowedToPostOptional = allowedToPostOptional >> Job.fromAsync

let mapTests =
testList "JobOption.map Tests" [
testCase "map with Job(Some x)" <| fun _ ->
Job.singleton (Some validTweet)
|> JobOption.map remainingCharacters
|> Expect.hasJobSomeValue 267

testCase "map with Job(None)" <| fun _ ->
Job.singleton (None)
|> JobOption.map remainingCharacters
|> Expect.hasJobNoneValue
]

let bindTests =
testList "JobOption.bind tests" [
testCase "bind with Job(Some x)" <| fun _ ->
allowedToPostOptional sampleUserId
|> JobOption.bind (fun isAllowed -> job {
if isAllowed then
return! createPostSome validCreatePostRequest
else
return None })
|> Expect.hasJobSomeValue (PostId newPostId)

testCase "bind with Job(None)" <| fun _ ->
allowedToPostOptional (UserId (Guid.NewGuid()))
|> JobOption.bind (fun isAllowed -> job {return Some isAllowed})
|> Expect.hasJobNoneValue

testCase "bind with Job(Ok x) that returns Job (None)" <| fun _ ->
allowedToPostOptional sampleUserId
|> JobOption.bind (fun _ -> job {
return None
})
|> Expect.hasJobNoneValue
]

let applyTests =
testList "JobOption.apply Tests" [
testCase "apply with Job(Some x)" <| fun _ ->
Job.singleton (Some validTweet)
|> JobOption.apply (Job.singleton (Some remainingCharacters))
|> Expect.hasJobSomeValue (267)

testCase "apply with Job(None)" <| fun _ ->
Job.singleton None
|> JobOption.apply (Job.singleton (Some remainingCharacters))
|> Expect.hasJobNoneValue
]

let retnTests =
testList "JobOption.retn Tests" [
testCase "retn with x" <| fun _ ->
JobOption.retn 267
|> Expect.hasJobSomeValue (267)
]

let jobOptionOperatorTests =
testList "JobOption Operators Tests" [
testCase "map & apply operators" <| fun _ ->
let getFollowersResult = getFollowersSome sampleUserId
let createPostResult = createPostSome validCreatePostRequest
newPostRequest <!> getFollowersResult <*> createPostResult
|> Expect.hasJobSomeValue {NewPostId = PostId newPostId; UserIds = followerIds}

testCase "bind operator" <| fun _ ->
allowedToPostOptional sampleUserId
>>= (fun isAllowed ->
if isAllowed then
createPostSome validCreatePostRequest
else
Job.singleton None)
|> Expect.hasJobSomeValue (PostId newPostId)
]

let allTests = testList "Job Option Tests" [
mapTests
bindTests
applyTests
retnTests
jobOptionOperatorTests
]
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
<Compile Include="Result.fs" />
<Compile Include="TaskResult.fs" />
<Compile Include="TaskResultCE.fs" />
<Compile Include="TaskOption.fs" />
<Compile Include="TaskOptionCE.fs" />
<Compile Include="List.fs" />
<Compile Include="TaskResultOption.fs" />
Expand Down
Loading

0 comments on commit 224955f

Please sign in to comment.