From 7ccd866be37f9485ac6368539404a6fc6a342950 Mon Sep 17 00:00:00 2001 From: Abel Braaksma Date: Sun, 6 Nov 2022 19:34:16 +0100 Subject: [PATCH] Add implementation for TaskSeq.delay --- README.md | 3 +- .../FSharp.Control.TaskSeq.Test.fsproj | 1 + .../TaskSeq.Delay.Tests.fs | 54 +++++++++++++++++++ src/FSharp.Control.TaskSeq/TaskSeq.fs | 5 ++ src/FSharp.Control.TaskSeq/TaskSeq.fsi | 8 +++ 5 files changed, 70 insertions(+), 1 deletion(-) create mode 100644 src/FSharp.Control.TaskSeq.Test/TaskSeq.Delay.Tests.fs diff --git a/README.md b/README.md index a102b443..10c1b46f 100644 --- a/README.md +++ b/README.md @@ -83,7 +83,7 @@ The following is the progress report: | | `compareWith` | `compareWith` | `compareWithAsync` | | | ✅ [#69][] | `concat` | `concat` | | | | ✅ [#70][] | `contains` | `contains` | | | -| | `delay` | `delay` | | | +| ✅ [#82][] | `delay` | `delay` | | | | | `distinct` | `distinct` | | | | | `distinctBy` | `dictinctBy` | `distinctByAsync` | | | ✅ [#2][] | `empty` | `empty` | | | @@ -610,4 +610,5 @@ module TaskSeq = [#70]: https://github.com/fsprojects/FSharp.Control.TaskSeq/pull/70 [#76]: https://github.com/fsprojects/FSharp.Control.TaskSeq/pull/76 [#81]: https://github.com/fsprojects/FSharp.Control.TaskSeq/pull/81 +[#82]: https://github.com/fsprojects/FSharp.Control.TaskSeq/pull/82 diff --git a/src/FSharp.Control.TaskSeq.Test/FSharp.Control.TaskSeq.Test.fsproj b/src/FSharp.Control.TaskSeq.Test/FSharp.Control.TaskSeq.Test.fsproj index 4ab3edd3..d030c085 100644 --- a/src/FSharp.Control.TaskSeq.Test/FSharp.Control.TaskSeq.Test.fsproj +++ b/src/FSharp.Control.TaskSeq.Test/FSharp.Control.TaskSeq.Test.fsproj @@ -18,6 +18,7 @@ + diff --git a/src/FSharp.Control.TaskSeq.Test/TaskSeq.Delay.Tests.fs b/src/FSharp.Control.TaskSeq.Test/TaskSeq.Delay.Tests.fs new file mode 100644 index 00000000..20026528 --- /dev/null +++ b/src/FSharp.Control.TaskSeq.Test/TaskSeq.Delay.Tests.fs @@ -0,0 +1,54 @@ +module TaskSeq.Tests.Delay + +open System + +open Xunit +open FsUnit.Xunit +open FsToolkit.ErrorHandling + +open FSharp.Control +open System.Collections.Generic + +// +// TaskSeq.delay +// + +let validateSequence ts = + ts + |> TaskSeq.toSeqCachedAsync + |> Task.map (Seq.map string) + |> Task.map (String.concat "") + |> Task.map (should equal "12345678910") + +module EmptySeq = + [)>] + let ``TaskSeq-delay with empty sequences`` variant = + fun () -> Gen.getEmptyVariant variant + |> TaskSeq.delay + |> verifyEmpty + +module Immutable = + [)>] + let ``TaskSeq-delay`` variant = + fun () -> Gen.getSeqImmutable variant + |> TaskSeq.delay + |> validateSequence + +module SideEffect = + [] + let ``TaskSeq-delay executes side effects`` () = task { + let mutable i = 0 + + let ts = + fun () -> taskSeq { + yield! [ 1..10 ] + i <- i + 1 + } + |> TaskSeq.delay + + do! ts |> validateSequence + i |> should equal 1 + let! len = TaskSeq.length ts + i |> should equal 2 // re-eval of the sequence executes side effect again + len |> should equal 10 + } diff --git a/src/FSharp.Control.TaskSeq/TaskSeq.fs b/src/FSharp.Control.TaskSeq/TaskSeq.fs index a9ac3bd9..e26ea68c 100644 --- a/src/FSharp.Control.TaskSeq/TaskSeq.fs +++ b/src/FSharp.Control.TaskSeq/TaskSeq.fs @@ -164,6 +164,11 @@ module TaskSeq = let initAsync count initializer = Internal.init (Some count) (InitActionAsync initializer) let initInfiniteAsync initializer = Internal.init None (InitActionAsync initializer) + let delay (generator: unit -> taskSeq<'T>) = + { new IAsyncEnumerable<'T> with + member _.GetAsyncEnumerator(ct) = generator().GetAsyncEnumerator(ct) + } + let concat (sources: taskSeq<#taskSeq<'T>>) = taskSeq { for ts in sources do yield! (ts :> taskSeq<'T>) diff --git a/src/FSharp.Control.TaskSeq/TaskSeq.fsi b/src/FSharp.Control.TaskSeq/TaskSeq.fsi index 8787e63e..6c367526 100644 --- a/src/FSharp.Control.TaskSeq/TaskSeq.fsi +++ b/src/FSharp.Control.TaskSeq/TaskSeq.fsi @@ -39,6 +39,14 @@ module TaskSeq = /// val lengthByAsync: predicate: ('T -> #Task) -> source: taskSeq<'T> -> Task + /// + /// Returns a task sequence that is given by the delayed specification of a task sequence. + /// + /// + /// The generating function for the task sequence. + /// The generated task sequence. + val delay: generator: (unit -> taskSeq<'T>) -> taskSeq<'T> + /// /// Generates a new task sequence which, when iterated, will return successive elements by calling the given function /// with the current index, up to the given count. Each element is saved after its initialization for successive access to