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 02c3b80d..5a5d0df0 100644
--- a/src/FSharp.Control.TaskSeq.Test/FSharp.Control.TaskSeq.Test.fsproj
+++ b/src/FSharp.Control.TaskSeq.Test/FSharp.Control.TaskSeq.Test.fsproj
@@ -50,6 +50,7 @@
+
diff --git a/src/FSharp.Control.TaskSeq.Test/TaskSeq.Using.Tests.fs b/src/FSharp.Control.TaskSeq.Test/TaskSeq.Using.Tests.fs
new file mode 100644
index 00000000..07c7c247
--- /dev/null
+++ b/src/FSharp.Control.TaskSeq.Test/TaskSeq.Using.Tests.fs
@@ -0,0 +1,106 @@
+module FSharp.Control.TaskSeq.Test
+
+open System
+open System.Threading.Tasks
+open FSharp.Control
+open FsUnit
+open Xunit
+
+
+type private OneGetter() =
+ member _.Get1() = 1
+
+type private Disposable(disposed: bool ref) =
+ inherit OneGetter()
+
+ interface IDisposable with
+ member _.Dispose() = disposed.Value <- true
+
+type private AsyncDisposable(disposed: bool ref) =
+ inherit OneGetter()
+
+ interface IAsyncDisposable with
+ member _.DisposeAsync() = ValueTask(task { do disposed.Value <- true })
+
+type private MultiDispose(disposed: int ref) =
+ inherit OneGetter()
+
+ interface IDisposable with
+ member _.Dispose() = disposed.Value <- 1
+
+ interface IAsyncDisposable with
+ member _.DisposeAsync() = ValueTask(task { do disposed.Value <- -1 })
+
+let private check = TaskSeq.length >> Task.map (should equal 1)
+
+[]
+let ``CE task: Using when type implements IDisposable`` () =
+ let disposed = ref false
+
+ let ts = taskSeq {
+ use x = new Disposable(disposed)
+ yield x.Get1()
+ }
+
+ check ts
+ |> Task.map (fun _ -> disposed.Value |> should be True)
+
+[]
+let ``CE task: Using when type implements IAsyncDisposable`` () =
+ let disposed = ref false
+
+ let ts = taskSeq {
+ use x = AsyncDisposable(disposed)
+ yield x.Get1()
+ }
+
+ check ts
+ |> Task.map (fun _ -> disposed.Value |> should be True)
+
+[]
+let ``CE task: Using when type implements IDisposable and IAsyncDisposable`` () =
+ let disposed = ref 0
+
+ let ts = taskSeq {
+ use x = new MultiDispose(disposed) // Used to fail to compile (see #97)
+ yield x.Get1()
+ }
+
+ check ts
+ |> Task.map (fun _ -> disposed.Value |> should equal -1) // should prefer IAsyncDisposable, which returns -1
+
+[]
+let ``CE task: Using! when type implements IDisposable`` () =
+ let disposed = ref false
+
+ let ts = taskSeq {
+ use! x = task { return new Disposable(disposed) }
+ yield x.Get1()
+ }
+
+ check ts
+ |> Task.map (fun _ -> disposed.Value |> should be True)
+
+[]
+let ``CE task: Using! when type implements IAsyncDisposable`` () =
+ let disposed = ref false
+
+ let ts = taskSeq {
+ use! x = task { return AsyncDisposable(disposed) }
+ yield x.Get1()
+ }
+
+ check ts
+ |> Task.map (fun _ -> disposed.Value |> should be True)
+
+[]
+let ``CE task: Using! when type implements IDisposable and IAsyncDisposable`` () =
+ let disposed = ref 0
+
+ let ts = taskSeq {
+ use! x = task { return new MultiDispose(disposed) } // Used to fail to compile (see #97)
+ yield x.Get1()
+ }
+
+ check ts
+ |> Task.map (fun _ -> disposed.Value |> should equal -1) // should prefer IAsyncDisposable, which returns -1
diff --git a/src/FSharp.Control.TaskSeq/AsyncExtensions.fs b/src/FSharp.Control.TaskSeq/AsyncExtensions.fs
index 51c46b4e..25817526 100644
--- a/src/FSharp.Control.TaskSeq/AsyncExtensions.fs
+++ b/src/FSharp.Control.TaskSeq/AsyncExtensions.fs
@@ -1,7 +1,5 @@
namespace FSharp.Control
-open FSharp.Control.TaskSeqBuilders
-
[]
module AsyncExtensions =
diff --git a/src/FSharp.Control.TaskSeq/AsyncExtensions.fsi b/src/FSharp.Control.TaskSeq/AsyncExtensions.fsi
index 7470eafd..cf96281e 100644
--- a/src/FSharp.Control.TaskSeq/AsyncExtensions.fsi
+++ b/src/FSharp.Control.TaskSeq/AsyncExtensions.fsi
@@ -2,7 +2,6 @@ namespace FSharp.Control
[]
module AsyncExtensions =
- open FSharp.Control.TaskSeqBuilders
type AsyncBuilder with
diff --git a/src/FSharp.Control.TaskSeq/FSharp.Control.TaskSeq.fsproj b/src/FSharp.Control.TaskSeq/FSharp.Control.TaskSeq.fsproj
index 1575392d..c276bfce 100644
--- a/src/FSharp.Control.TaskSeq/FSharp.Control.TaskSeq.fsproj
+++ b/src/FSharp.Control.TaskSeq/FSharp.Control.TaskSeq.fsproj
@@ -24,10 +24,11 @@ Generates optimized IL code through the new resumable state machines, and comes
Release notes:
0.3.0 (unreleased)
- - adds support for 'for .. in ..' with task sequences in F# tasks and async, #75, #93 and #99 (with help from @theangrybyrd)
- - adds TaskSeq.singleton, #90 (by @gusty)
- - improves TaskSeq.empty by not relying on resumable state, #89 (by @gusty)
- - does not throw exceptions anymore for unequal lengths in TaskSeq.zip, fixes #32
+ - adds support for 'for .. in ..' with task sequences in F# tasks and async, #75, #93 and #99 (with help from @theangrybyrd).
+ - adds TaskSeq.singleton, #90 (by @gusty).
+ - fixes overload resolution bug with 'use' and 'use!', #97 (thanks @peterfaria).
+ - improves TaskSeq.empty by not relying on resumable state, #89 (by @gusty).
+ - does not throw exceptions anymore for unequal lengths in TaskSeq.zip, fixes #32.
0.2.2
- removes TaskSeq.toSeqCachedAsync, which was incorrectly named. Use toSeq or toListAsync instead.
- renames TaskSeq.toSeqCached to TaskSeq.toSeq, which was its actual operational behavior.
diff --git a/src/FSharp.Control.TaskSeq/TaskExtensions.fs b/src/FSharp.Control.TaskSeq/TaskExtensions.fs
index aa40599f..b168f358 100644
--- a/src/FSharp.Control.TaskSeq/TaskExtensions.fs
+++ b/src/FSharp.Control.TaskSeq/TaskExtensions.fs
@@ -7,8 +7,6 @@ open System.Threading.Tasks
open Microsoft.FSharp.Core.CompilerServices
open Microsoft.FSharp.Core.LanguagePrimitives.IntrinsicOperators
-open FSharp.Control.TaskSeqBuilders
-
#nowarn "57"
#nowarn "1204"
#nowarn "3513"
diff --git a/src/FSharp.Control.TaskSeq/TaskExtensions.fsi b/src/FSharp.Control.TaskSeq/TaskExtensions.fsi
index b5a97e7d..76cd8f9d 100644
--- a/src/FSharp.Control.TaskSeq/TaskExtensions.fsi
+++ b/src/FSharp.Control.TaskSeq/TaskExtensions.fsi
@@ -4,7 +4,6 @@ namespace FSharp.Control
[]
module TaskExtensions =
- open FSharp.Control.TaskSeqBuilders
type TaskBuilder with
diff --git a/src/FSharp.Control.TaskSeq/TaskSeq.fs b/src/FSharp.Control.TaskSeq/TaskSeq.fs
index e1a13d1b..05f5313c 100644
--- a/src/FSharp.Control.TaskSeq/TaskSeq.fs
+++ b/src/FSharp.Control.TaskSeq/TaskSeq.fs
@@ -7,8 +7,6 @@ open System.Threading.Tasks
#nowarn "57"
module TaskSeq =
- // F# BUG: the following module is 'AutoOpen' and this isn't needed in the Tests project. Why do we need to open it?
- open FSharp.Control.TaskSeqBuilders
// Just for convenience
module Internal = TaskSeqInternal
diff --git a/src/FSharp.Control.TaskSeq/TaskSeq.fsi b/src/FSharp.Control.TaskSeq/TaskSeq.fsi
index 172ab83c..1f0f1497 100644
--- a/src/FSharp.Control.TaskSeq/TaskSeq.fsi
+++ b/src/FSharp.Control.TaskSeq/TaskSeq.fsi
@@ -5,7 +5,6 @@ namespace FSharp.Control
module TaskSeq =
open System.Collections.Generic
open System.Threading.Tasks
- open FSharp.Control.TaskSeqBuilders
/// Initialize an empty taskSeq.
val empty<'T> : taskSeq<'T>
diff --git a/src/FSharp.Control.TaskSeq/TaskSeqBuilder.fs b/src/FSharp.Control.TaskSeq/TaskSeqBuilder.fs
index 195ed87b..a372b14f 100644
--- a/src/FSharp.Control.TaskSeq/TaskSeqBuilder.fs
+++ b/src/FSharp.Control.TaskSeq/TaskSeqBuilder.fs
@@ -1,4 +1,4 @@
-namespace FSharp.Control.TaskSeqBuilders
+namespace FSharp.Control
open System.Diagnostics
@@ -47,13 +47,6 @@ module Internal = // cannot be marked with 'internal' scope
type taskSeq<'T> = IAsyncEnumerable<'T>
-type IPriority1 =
- interface
- end
-
-type IPriority2 =
- interface
- end
[]
type TaskSeqStateMachineData<'T>() =
@@ -489,34 +482,7 @@ type TaskSeqBuilder() =
true)
)
- member inline this.Using
- (
- disp: #IDisposable,
- body: #IDisposable -> TaskSeqCode<'T>,
- ?priority: IPriority2
- ) : TaskSeqCode<'T> =
-
- // FIXME: what about priority?
- ignore priority
-
- // A using statement is just a try/finally with the finally block disposing if non-null.
- this.TryFinally(
- (fun sm -> (body disp).Invoke(&sm)),
- (fun () ->
- // yes, this can be null from time to time
- if not (isNull (box disp)) then
- disp.Dispose())
- )
-
- member inline this.Using
- (
- disp: #IAsyncDisposable,
- body: #IAsyncDisposable -> TaskSeqCode<'T>,
- ?priority: IPriority1
- ) : TaskSeqCode<'T> =
-
- // FIXME: what about priorities?
- ignore priority
+ member inline this.Using(disp: #IAsyncDisposable, body: #IAsyncDisposable -> TaskSeqCode<'T>) : TaskSeqCode<'T> =
// A using statement is just a try/finally with the finally block disposing if non-null.
this.TryFinallyAsync(
@@ -528,23 +494,6 @@ type TaskSeqBuilder() =
Task.CompletedTask)
)
- member inline this.For(sequence: seq<'TElement>, body: 'TElement -> TaskSeqCode<'T>) : TaskSeqCode<'T> =
- // A for loop is just a using statement on the sequence's enumerator...
- this.Using(
- sequence.GetEnumerator(),
- // ... and its body is a while loop that advances the enumerator and runs the body on each element.
- (fun e -> this.While((fun () -> e.MoveNext()), (fun sm -> (body e.Current).Invoke(&sm))))
- )
-
- member inline this.For(source: #IAsyncEnumerable<'TElement>, body: 'TElement -> TaskSeqCode<'T>) : TaskSeqCode<'T> =
- TaskSeqCode<'T>(fun sm ->
- this
- .Using(
- source.GetAsyncEnumerator(sm.Data.cancellationToken),
- (fun e -> this.WhileAsync((fun () -> e.MoveNextAsync()), (fun sm -> (body e.Current).Invoke(&sm))))
- )
- .Invoke(&sm))
-
member inline _.Yield(v: 'T) : TaskSeqCode<'T> =
TaskSeqCode<'T>(fun sm ->
// This will yield with __stack_fin = false
@@ -556,11 +505,6 @@ type TaskSeqBuilder() =
sm.Data.awaiter <- null
__stack_fin)
- member inline this.YieldFrom(source: IAsyncEnumerable<'T>) : TaskSeqCode<'T> =
- this.For(source, (fun v -> this.Yield(v)))
-
- member inline this.YieldFrom(source: seq<'T>) : TaskSeqCode<'T> = this.For(source, (fun v -> this.Yield(v)))
-
member inline _.Bind(task: Task<'TResult1>, continuation: ('TResult1 -> TaskSeqCode<'T>)) : TaskSeqCode<'T> =
TaskSeqCode<'T>(fun sm ->
let mutable awaiter = task.GetAwaiter()
@@ -610,3 +554,57 @@ type TaskSeqBuilder() =
sm.Data.awaiter <- awaiter
sm.Data.current <- ValueNone
false)
+
+//
+// These "modules of priority" allow for an indecisive F# to resolve
+// the proper overload if a single type implements more than one
+// interface. For instance, a type implementing 'IDisposable' and
+// 'IAsyncDisposable'.
+//
+// See for more info tasks.fs in F# Core.
+//
+// This section also includes the dependencies of such overloads
+// (like For depending on Using etc).
+//
+
+[]
+module MediumPriority =
+ type TaskSeqBuilder with
+
+ member inline this.Using(disp: #IDisposable, body: #IDisposable -> TaskSeqCode<'T>) : TaskSeqCode<'T> =
+
+ // A using statement is just a try/finally with the finally block disposing if non-null.
+ this.TryFinally(
+ (fun sm -> (body disp).Invoke(&sm)),
+ (fun () ->
+ // yes, this can be null from time to time
+ if not (isNull (box disp)) then
+ disp.Dispose())
+ )
+
+ member inline this.For(sequence: seq<'TElement>, body: 'TElement -> TaskSeqCode<'T>) : TaskSeqCode<'T> =
+ // A for loop is just a using statement on the sequence's enumerator...
+ this.Using(
+ sequence.GetEnumerator(),
+ // ... and its body is a while loop that advances the enumerator and runs the body on each element.
+ (fun e -> this.While((fun () -> e.MoveNext()), (fun sm -> (body e.Current).Invoke(&sm))))
+ )
+
+ member inline this.YieldFrom(source: seq<'T>) : TaskSeqCode<'T> = this.For(source, (fun v -> this.Yield(v)))
+
+ member inline this.For
+ (
+ source: #IAsyncEnumerable<'TElement>,
+ body: 'TElement -> TaskSeqCode<'T>
+ ) : TaskSeqCode<'T> =
+ TaskSeqCode<'T>(fun sm ->
+ this
+ .Using(
+ source.GetAsyncEnumerator(sm.Data.cancellationToken),
+ (fun e ->
+ this.WhileAsync((fun () -> e.MoveNextAsync()), (fun sm -> (body e.Current).Invoke(&sm))))
+ )
+ .Invoke(&sm))
+
+ member inline this.YieldFrom(source: IAsyncEnumerable<'T>) : TaskSeqCode<'T> =
+ this.For(source, (fun v -> this.Yield(v)))
diff --git a/src/FSharp.Control.TaskSeq/TaskSeqInternal.fs b/src/FSharp.Control.TaskSeq/TaskSeqInternal.fs
index e2f2293a..deeaefbd 100644
--- a/src/FSharp.Control.TaskSeq/TaskSeqInternal.fs
+++ b/src/FSharp.Control.TaskSeq/TaskSeqInternal.fs
@@ -4,7 +4,6 @@ open System
open System.Collections.Generic
open System.Threading
open System.Threading.Tasks
-open FSharp.Control.TaskSeqBuilders
[]
module ExtraTaskSeqOperators =