Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added Async, Task and Job overloads for the relavant Option CEs to resolve #117 #132

Merged
merged 3 commits into from
May 26, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 45 additions & 36 deletions src/FsToolkit.ErrorHandling.JobResult/JobOptionCE.fs
Original file line number Diff line number Diff line change
Expand Up @@ -12,31 +12,11 @@ module JobOptionCE =
member __.Return (value: 'T) : Job<Option<_>> =
job.Return <| option.Return value

member __.ReturnFrom
(asyncResult: Async<Option<_>>)
: Job<Option<_>> =
asyncResult |> Job.fromAsync

member __.ReturnFrom
(jobResult: Job<Option<_>>)
: Job<Option<_>> =
jobResult

member __.ReturnFrom
(taskResult: Task<Option<_>>)
: Job<Option<_>> =
Job.awaitTask taskResult

member __.ReturnFrom
(taskResult: unit -> Task<Option<_>>)
: Job<Option<_>> =
Job.fromTask taskResult

member __.ReturnFrom
(result: Option<_>)
: Job<Option<_>> =
job.Return result

member __.Zero () : Job<Option<_>> =
job.Return <| option.Zero ()

Expand All @@ -50,29 +30,13 @@ module JobOptionCE =
| Some x -> return! binder x
| None -> return None
}
member this.Bind
(asyncResult: Async<Option<_>>,
binder: 'T -> Job<Option<_>>)
: Job<Option<_>> =
this.Bind(Job.fromAsync asyncResult, binder)

member this.Bind
(taskResult: Task<Option<_>>,
binder: 'T -> Job<Option<_>>)
: Job<Option<_>> =
this.Bind(Job.awaitTask taskResult, binder)

member this.Bind
(taskResult: unit -> Task<Option<_>>,
binder: 'T -> Job<Option<_>>)
: Job<Option<_>> =
this.Bind(Job.fromTask taskResult, binder)

member this.Bind
(result: Option<_>, binder: 'T -> Job<Option<_>>)
: Job<Option<_>> =
this.Bind(this.ReturnFrom result, binder)

member __.Delay
(generator: unit -> Job<Option<_>>)
: Job<Option<_>> =
Expand Down Expand Up @@ -127,4 +91,49 @@ module JobOptionCE =
this.While(enum.MoveNext,
this.Delay(fun () -> binder enum.Current)))

/// <summary>
/// Method lets us transform data types into our internal representation. This is the identity method to recognize the self type.
///
/// See https://stackoverflow.com/questions/35286541/why-would-you-use-builder-source-in-a-custom-computation-expression-builder
/// </summary>
member inline _.Source(job : Job<Option<_>>) : Job<Option<_>> = job

/// <summary>
/// Method lets us transform data types into our internal representation.
/// </summary>
member inline _.Source(async : Async<Option<_>>) : Job<Option<_>> = async |> Job.fromAsync

/// <summary>
/// Method lets us transform data types into our internal representation.
/// </summary>
member inline _.Source(task : Task<Option<_>>) : Job<Option<_>> = task |> Job.awaitTask

let jobOption = JobOptionBuilder()

[<AutoOpen>]
// Having members as extensions gives them lower priority in
// overload resolution and allows skipping more type annotations.
module JobOptionCEExtensions =

type JobOptionBuilder with
/// <summary>
/// Needed to allow `for..in` and `for..do` functionality
/// </summary>
member inline __.Source(s: #seq<_>) = s

/// <summary>
/// Method lets us transform data types into our internal representation.
/// </summary>
member inline __.Source(r: Option<'t>) = Job.singleton r
/// <summary>
/// Method lets us transform data types into our internal representation.
/// </summary>
member inline __.Source(a: Job<'t>) = a |> Job.map Some
/// <summary>
/// Method lets us transform data types into our internal representation.
/// </summary>
member inline __.Source(a: Async<'t>) = a |> Job.fromAsync |> Job.map Some
/// <summary>
/// Method lets us transform data types into our internal representation.
/// </summary>
member inline __.Source(a: Task<'t>) = a |> Job.awaitTask |> Job.map Some
60 changes: 36 additions & 24 deletions src/FsToolkit.ErrorHandling.TaskResult/TaskOptionCE.fs
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,6 @@ module TaskOptionCE =
: Ply<Option<_>> =
uply.ReturnFrom taskResult

member inline this.ReturnFrom
(asyncResult: Async<Option<_>>)
: Ply<Option<_>> =
this.ReturnFrom (Async.StartAsTask asyncResult)

member inline _.ReturnFrom
(result: Option<_>)
: Ply<Option<_>> =
uply.Return result

member inline _.Zero () : Ply<Option<_>> =
uply.Return <| option.Zero()

Expand All @@ -40,20 +30,6 @@ module TaskOptionCE =
| Some x -> binder x
| None -> uply.Return None
uply.Bind(taskResult, binder')

member inline this.Bind
(asyncResult: Async<Option<_>>,
binder: 'T -> Ply<Option<_>>)
: Ply<Option<_>> =
this.Bind(Async.StartAsTask asyncResult, binder)

member inline this.Bind
(result: Option<_>, binder: 'T -> Ply<Option<_>>)
: Ply<Option<_>> =
let result =
result
|> Task.singleton
this.Bind(result, binder)

member inline _.Delay
(generator: unit -> Ply<Option<_>>) =
Expand Down Expand Up @@ -117,4 +93,40 @@ module TaskOptionCE =

member inline _.Run(f : unit -> Ply<'m>) = task.Run f

/// <summary>
/// Method lets us transform data types into our internal representation. This is the identity method to recognize the self type.
/// See https://stackoverflow.com/questions/35286541/why-would-you-use-builder-source-in-a-custom-computation-expression-builder
/// </summary>
member inline _.Source(task : Task<Option<_>>) : Task<Option<_>> = task

/// <summary>
/// Method lets us transform data types into our internal representation.
/// </summary>
member inline _.Source(async : Async<Option<_>>) : Task<Option<_>> = async |> Async.StartAsTask

let taskOption = TaskOptionBuilder()

[<AutoOpen>]
// Having members as extensions gives them lower priority in
// overload resolution and allows skipping more type annotations.
module TaskOptionCEExtensions =

type TaskOptionBuilder with
/// <summary>
/// Needed to allow `for..in` and `for..do` functionality
/// </summary>
member inline __.Source(s: #seq<_>) = s

/// <summary>
/// Method lets us transform data types into our internal representation.
/// </summary>
member inline __.Source(r: Option<'t>) = Task.singleton r
/// <summary>
/// Method lets us transform data types into our internal representation.
/// </summary>
member inline __.Source(a: Task<'t>) = a |> Task.map Some

/// <summary>
/// Method lets us transform data types into our internal representation.
/// </summary>
member inline __.Source(a: Async<'t>) = a |> Async.StartAsTask |> Task.map Some
71 changes: 43 additions & 28 deletions src/FsToolkit.ErrorHandling/AsyncOptionCE.fs
Original file line number Diff line number Diff line change
Expand Up @@ -15,22 +15,10 @@ module AsyncOptionCE =
: Async<Option<_>> =
asyncResult

#if !FABLE_COMPILER
member __.ReturnFrom
(taskResult: Task<Option<_>>)
: Async<Option<_>> =
Async.AwaitTask taskResult
#endif

member __.ReturnFrom
(result: Option<_>)
: Async<Option<_>> =
async.Return result

member __.Zero () : Async<Option<_>> =
async.Return <| option.Zero ()

member __.Bind
member inline __.Bind
(asyncResult: Async<Option<_>>,
binder: 'T -> Async<Option<_>>)
: Async<Option<_>> =
Expand All @@ -41,20 +29,6 @@ module AsyncOptionCE =
| None -> return None
}

#if !FABLE_COMPILER
member this.Bind
(taskResult: Task<Option<_>>,
binder: 'T -> Async<Option<_>>)
: Async<Option<_>> =
this.Bind(Async.AwaitTask taskResult, binder)
#endif

member this.Bind
(result: Option<_>, binder: 'T -> Async<Option<_>>)
: Async<Option<_>> =
this.Bind(this.ReturnFrom result, binder)


member __.Delay
(generator: unit -> Async<Option<_>>)
: Async<Option<_>> =
Expand Down Expand Up @@ -100,5 +74,46 @@ module AsyncOptionCE =
this.While(enum.MoveNext,
this.Delay(fun () -> binder enum.Current)))

/// <summary>
/// Method lets us transform data types into our internal representation. This is the identity method to recognize the self type.
///
/// See https://stackoverflow.com/questions/35286541/why-would-you-use-builder-source-in-a-custom-computation-expression-builder
/// </summary>
member inline _.Source(async : Async<Option<_>>) : Async<Option<_>> = async

let asyncOption = AsyncOptionBuilder()
#if !FABLE_COMPILER
/// <summary>
/// Method lets us transform data types into our internal representation.
/// </summary>
member inline _.Source(task : Task<Option<_>>) : Async<Option<_>> = task |> Async.AwaitTask
#endif

let asyncOption = AsyncOptionBuilder()


[<AutoOpen>]
// Having members as extensions gives them lower priority in
// overload resolution and allows skipping more type annotations.
module AsyncOptionCEExtensions =

type AsyncOptionBuilder with
/// <summary>
/// Needed to allow `for..in` and `for..do` functionality
/// </summary>
member inline __.Source(s: #seq<_>) = s

/// <summary>
/// Method lets us transform data types into our internal representation.
/// </summary>
member inline __.Source(r: Option<'t>) = Async.singleton r
/// <summary>
/// Method lets us transform data types into our internal representation.
/// </summary>
member inline __.Source(a: Async<'t>) = a |> Async.map Some

#if !FABLE_COMPILER
/// <summary>
/// Method lets us transform data types into our internal representation.
/// </summary>
member inline __.Source(a: Task<'t>) = a |> Async.AwaitTask |> Async.map Some
#endif
37 changes: 34 additions & 3 deletions tests/FsToolkit.ErrorHandling.JobResult.Tests/JobOptionCE.fs
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,21 @@ let ceTests =
}
Expect.equal actual expected "Should return value wrapped in option"
}

testCaseJob "ReturnFrom Async None" <| job {
let expected = None
let! actual = jobOption {
return! (async.Return None)
}
Expect.equal actual expected "Should return value wrapped in option"
}
testCaseJob "ReturnFrom Async" <| job {
let expected = Some 42
let! actual = jobOption {
return! (async.Return 42)
}
Expect.equal actual expected "Should return value wrapped in option"
}

testCaseJob "ReturnFrom Task None" <| job {
let expected = None
Expand All @@ -51,9 +58,9 @@ let ceTests =
}

testCaseJob "ReturnFrom Job None" <| job {
let expected = None
let expected = Some 42
let! actual = jobOption {
return! (Job.result None)
return! (Job.result (Some 42))
}
Expect.equal actual expected "Should return value wrapped in option"
}
Expand Down Expand Up @@ -82,6 +89,14 @@ let ceTests =
}
Expect.equal actual expected "Should bind value wrapped in option"
}
testCaseJob "Bind Async" <| job {
let expected = Some 42
let! actual = jobOption {
let! value = async.Return 42
return value
}
Expect.equal actual expected "Should bind value wrapped in option"
}
testCaseJob "Bind Task None" <| job {
let expected = None
let! actual = jobOption {
Expand All @@ -90,6 +105,14 @@ let ceTests =
}
Expect.equal actual expected "Should bind value wrapped in option"
}
testCaseJob "Bind Task" <| job {
let expected = Some 42
let! actual = jobOption {
let! value = Task.FromResult 42
return value
}
Expect.equal actual expected "Should bind value wrapped in option"
}

testCaseJob "Bind Job None" <| job {
let expected = None
Expand All @@ -99,6 +122,14 @@ let ceTests =
}
Expect.equal actual expected "Should bind value wrapped in option"
}
testCaseJob "Bind Job" <| job {
let expected = Some 42
let! actual = jobOption {
let! value = Job.result 42
return value
}
Expect.equal actual expected "Should bind value wrapped in option"
}

testCaseJob "Zero/Combine/Delay/Run" <| job {
let data = 42
Expand Down
Loading