Skip to content

Commit 1bd3a22

Browse files
committed
Implement Option.traverseAsync and Option.sequenceAsync and add tests
1 parent 1d2d310 commit 1bd3a22

File tree

2 files changed

+84
-0
lines changed

2 files changed

+84
-0
lines changed

src/FsToolkit.ErrorHandling/Option.fs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,28 @@ module Option =
321321

322322
opt
323323

324+
/// Converts a Option<Async<_>> to an Async<Option<_>>
325+
let inline sequenceAsync (optAsync: Option<Async<'T>>) : Async<Option<'T>> =
326+
async {
327+
match optAsync with
328+
| Some asnc ->
329+
let! x = asnc
330+
return Some x
331+
| None -> return None
332+
}
333+
334+
/// <summary>
335+
/// Maps an Async function over an Option, returning an Async Option.
336+
/// </summary>
337+
/// <param name="f">The function to map over the Option.</param>
338+
/// <param name="opt">The Option to map over.</param>
339+
/// <returns>An Async Option with the mapped value.</returns>
340+
let inline traverseAsync
341+
([<InlineIfLambda>] f: 'T -> Async<'T>)
342+
(opt: Option<'T>)
343+
: Async<Option<'T>> =
344+
sequenceAsync ((map f) opt)
345+
324346
/// <summary>
325347
/// Creates an option from a boolean value and a value of type 'a.
326348
/// If the boolean value is true, returns <c>Some</c> value.

tests/FsToolkit.ErrorHandling.Tests/Option.fs

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,66 @@ let teeIfTests =
184184
Expect.equal foo "foo" ""
185185
]
186186

187+
let sequenceAsyncTests =
188+
testList "Option.sequenceAsync Tests" [
189+
testCaseAsync "sequenceAsync returns the async value if Some"
190+
<| async {
191+
let optAsync =
192+
async { return "foo" }
193+
|> Some
194+
195+
let! value =
196+
optAsync
197+
|> Option.sequenceAsync
198+
199+
Expect.equal value (Some "foo") ""
200+
}
201+
202+
testCaseAsync "sequenceAsync returns None if None"
203+
<| async {
204+
let optAsync = None
205+
206+
let! value =
207+
optAsync
208+
|> Option.sequenceAsync
209+
210+
Expect.equal value None ""
211+
}
212+
]
213+
214+
let traverseAsyncTests =
215+
testList "Option.traverseAsync Tests" [
216+
testCaseAsync "traverseAsync returns the async value if Some"
217+
<| async {
218+
let optAsync = Some "foo"
219+
220+
let optFunc =
221+
id
222+
>> Async.singleton
223+
224+
let! value =
225+
(optFunc, optAsync)
226+
||> Option.traverseAsync
227+
228+
Expect.equal value (Some "foo") ""
229+
}
230+
231+
testCaseAsync "traverseAsync returns None if None"
232+
<| async {
233+
let optAsync = None
234+
235+
let optFunc =
236+
id
237+
>> Async.singleton
238+
239+
let! value =
240+
(optFunc, optAsync)
241+
||> Option.traverseAsync
242+
243+
Expect.equal value None ""
244+
}
245+
]
246+
187247
let traverseResultTests =
188248
testList "Option.traverseResult Tests" [
189249
testCase "traverseResult with Some of valid data"
@@ -366,6 +426,8 @@ let optionOperatorsTests =
366426

367427
let allTests =
368428
testList "Option Tests" [
429+
sequenceAsyncTests
430+
traverseAsyncTests
369431
traverseResultTests
370432
tryParseTests
371433
tryGetValueTests

0 commit comments

Comments
 (0)