Skip to content

Commit b2f6b41

Browse files
JmaharmanTheAngryByrd
authored andcommitted
Converted AsyncOptionCE to use Source overload and added Async/Task
1 parent 6bea86c commit b2f6b41

File tree

2 files changed

+73
-28
lines changed

2 files changed

+73
-28
lines changed

src/FsToolkit.ErrorHandling/AsyncOptionCE.fs

Lines changed: 43 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -15,22 +15,10 @@ module AsyncOptionCE =
1515
: Async<Option<_>> =
1616
asyncResult
1717

18-
#if !FABLE_COMPILER
19-
member __.ReturnFrom
20-
(taskResult: Task<Option<_>>)
21-
: Async<Option<_>> =
22-
Async.AwaitTask taskResult
23-
#endif
24-
25-
member __.ReturnFrom
26-
(result: Option<_>)
27-
: Async<Option<_>> =
28-
async.Return result
29-
3018
member __.Zero () : Async<Option<_>> =
3119
async.Return <| option.Zero ()
3220

33-
member __.Bind
21+
member inline __.Bind
3422
(asyncResult: Async<Option<_>>,
3523
binder: 'T -> Async<Option<_>>)
3624
: Async<Option<_>> =
@@ -41,20 +29,6 @@ module AsyncOptionCE =
4129
| None -> return None
4230
}
4331

44-
#if !FABLE_COMPILER
45-
member this.Bind
46-
(taskResult: Task<Option<_>>,
47-
binder: 'T -> Async<Option<_>>)
48-
: Async<Option<_>> =
49-
this.Bind(Async.AwaitTask taskResult, binder)
50-
#endif
51-
52-
member this.Bind
53-
(result: Option<_>, binder: 'T -> Async<Option<_>>)
54-
: Async<Option<_>> =
55-
this.Bind(this.ReturnFrom result, binder)
56-
57-
5832
member __.Delay
5933
(generator: unit -> Async<Option<_>>)
6034
: Async<Option<_>> =
@@ -100,5 +74,46 @@ module AsyncOptionCE =
10074
this.While(enum.MoveNext,
10175
this.Delay(fun () -> binder enum.Current)))
10276

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

104-
let asyncOption = AsyncOptionBuilder()
84+
#if !FABLE_COMPILER
85+
/// <summary>
86+
/// Method lets us transform data types into our internal representation.
87+
/// </summary>
88+
member inline _.Source(task : Task<Option<_>>) : Async<Option<_>> = task |> Async.AwaitTask
89+
#endif
90+
91+
let asyncOption = AsyncOptionBuilder()
92+
93+
94+
[<AutoOpen>]
95+
// Having members as extensions gives them lower priority in
96+
// overload resolution and allows skipping more type annotations.
97+
module AsyncOptionCEExtensions =
98+
99+
type AsyncOptionBuilder with
100+
/// <summary>
101+
/// Needed to allow `for..in` and `for..do` functionality
102+
/// </summary>
103+
member inline __.Source(s: #seq<_>) = s
104+
105+
/// <summary>
106+
/// Method lets us transform data types into our internal representation.
107+
/// </summary>
108+
member inline __.Source(r: Option<'t>) = Async.singleton r
109+
/// <summary>
110+
/// Method lets us transform data types into our internal representation.
111+
/// </summary>
112+
member inline __.Source(a: Async<'t>) = a |> Async.map Some
113+
114+
#if !FABLE_COMPILER
115+
/// <summary>
116+
/// Method lets us transform data types into our internal representation.
117+
/// </summary>
118+
member inline __.Source(a: Task<'t>) = a |> Async.AwaitTask |> Async.map Some
119+
#endif

tests/FsToolkit.ErrorHandling.Tests/AsyncOptionCE.fs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,13 @@ let ceTests =
4444
}
4545
Expect.equal actual expected "Should return value wrapped in option"
4646
}
47+
testCaseAsync "ReturnFrom Async" <| async {
48+
let expected = Some 42
49+
let! actual = asyncOption {
50+
return! async { return 42 }
51+
}
52+
Expect.equal actual expected "Should return value wrapped in option"
53+
}
4754

4855
#if !FABLE_COMPILER
4956
testCaseAsync "ReturnFrom Task None" <| async {
@@ -53,6 +60,13 @@ let ceTests =
5360
}
5461
Expect.equal actual expected "Should return value wrapped in option"
5562
}
63+
testCaseAsync "ReturnFrom Task" <| async {
64+
let expected = Some 42
65+
let! actual = asyncOption {
66+
return! (Task.FromResult 42)
67+
}
68+
Expect.equal actual expected "Should return value wrapped in option"
69+
}
5670
#endif
5771
testCaseAsync "Bind Some" <| async {
5872
let expected = Some 42
@@ -78,6 +92,14 @@ let ceTests =
7892
}
7993
Expect.equal actual expected "Should bind value wrapped in option"
8094
}
95+
testCaseAsync "Bind Async" <| async {
96+
let expected = Some 42
97+
let! actual = asyncOption {
98+
let! value = async.Return(42)
99+
return value
100+
}
101+
Expect.equal actual expected "Should bind value wrapped in option"
102+
}
81103

82104
#if !FABLE_COMPILER
83105
testCaseAsync "Bind Task None" <| async {
@@ -88,6 +110,14 @@ let ceTests =
88110
}
89111
Expect.equal actual expected "Should bind value wrapped in option"
90112
}
113+
testCaseAsync "Bind Task" <| async {
114+
let expected = Some 42
115+
let! actual = asyncOption {
116+
let! value = Task.FromResult 42
117+
return value
118+
}
119+
Expect.equal actual expected "Should bind value wrapped in option"
120+
}
91121
#endif
92122

93123
testCaseAsync "Zero/Combine/Delay/Run" <| async {

0 commit comments

Comments
 (0)