Skip to content

Commit 4fd12b3

Browse files
committed
AsyncSeq.traverseOptionAsync / traverseChoiceAsync
1 parent f147b98 commit 4fd12b3

2 files changed

Lines changed: 57 additions & 0 deletions

File tree

src/FSharpx.Async/AsyncSeq.fs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ namespace FSharpx.Control
77
open System
88
open System.Threading
99
open System.IO
10+
open FSharpx.Control.Utils
1011

1112
// ----------------------------------------------------------------------------
1213

@@ -474,6 +475,34 @@ module AsyncSeq =
474475
let inline zapp (fs:AsyncSeq<'a -> 'b>) (s:AsyncSeq<'a>) : AsyncSeq<'b> =
475476
zipWith ((|>)) s fs
476477

478+
/// Traverses an async sequence an applies to specified function such that if None is returned the traversal short-circuits
479+
/// and None is returned as the result. Otherwise, the entire sequence is traversed and the result returned as Some.
480+
let rec traverseOptionAsync (f:'a -> Async<'b option>) (s:AsyncSeq<'a>) : Async<AsyncSeq<'b> option> = async {
481+
let! s = s
482+
match s with
483+
| Nil -> return Some (Nil |> async.Return)
484+
| Cons(a,tl) ->
485+
let! b = f a
486+
match b with
487+
| Some b ->
488+
return! traverseOptionAsync f tl |> Async.map (Option.map (fun tl -> Cons(b, tl) |> async.Return))
489+
| None ->
490+
return None }
491+
492+
/// Traverses an async sequence an applies to specified function such that if Choice2Of2 is returned the traversal short-circuits
493+
/// and Choice2Of2 is returned as the result. Otherwise, the entire sequence is traversed and the result returned as Choice1Of2.
494+
let rec traverseChoiceAsync (f:'a -> Async<Choice<'b, 'e>>) (s:AsyncSeq<'a>) : Async<Choice<AsyncSeq<'b>, 'e>> = async {
495+
let! s = s
496+
match s with
497+
| Nil -> return Choice1Of2 (Nil |> async.Return)
498+
| Cons(a,tl) ->
499+
let! b = f a
500+
match b with
501+
| Choice1Of2 b ->
502+
return! traverseChoiceAsync f tl |> Async.map (Choice.mapl (fun tl -> Cons(b, tl) |> async.Return))
503+
| Choice2Of2 e ->
504+
return Choice2Of2 e }
505+
477506
/// Returns an async computation which iterates the async sequence for
478507
/// its side-effects.
479508
let inline runAsync (s:AsyncSeq<unit>) : Async<unit> =

tests/FSharpx.Async.Tests/AsyncSeqTests.fs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,3 +209,31 @@ let ``AsyncSeq.replicate``() =
209209
let expected = List.replicate c x |> AsyncSeq.ofSeq
210210
Assert.True(EQ expected actual)
211211

212+
[<Test>]
213+
let ``AsyncSeq.traverseOptionAsync``() =
214+
let seen = ResizeArray<_>()
215+
let s = [1;2;3;4;5] |> AsyncSeq.ofSeq
216+
let f i =
217+
seen.Add i
218+
if i < 2 then Some i |> async.Return
219+
else None |> async.Return
220+
let r = AsyncSeq.traverseOptionAsync f s |> Async.RunSynchronously
221+
match r with
222+
| Some _ -> Assert.Fail()
223+
| None -> Assert.True(([1;2] = (seen |> List.ofSeq)))
224+
225+
[<Test>]
226+
let ``AsyncSeq.traverseChoiceAsync``() =
227+
let seen = ResizeArray<_>()
228+
let s = [1;2;3;4;5] |> AsyncSeq.ofSeq
229+
let f i =
230+
seen.Add i
231+
if i < 2 then Choice1Of2 i |> async.Return
232+
else Choice2Of2 "oh no" |> async.Return
233+
let r = AsyncSeq.traverseChoiceAsync f s |> Async.RunSynchronously
234+
match r with
235+
| Choice1Of2 _ -> Assert.Fail()
236+
| Choice2Of2 e ->
237+
Assert.AreEqual("oh no", e)
238+
Assert.True(([1;2] = (seen |> List.ofSeq)))
239+

0 commit comments

Comments
 (0)