diff --git a/release-notes.txt b/release-notes.txt
index 20ee8fb..49639e6 100644
--- a/release-notes.txt
+++ b/release-notes.txt
@@ -2,6 +2,7 @@
Release notes:
Unreleased
+ - fixes: `Async.bind` signature corrected from `(Async<'T> -> Async<'U>)` to `('T -> Async<'U>)` to match standard monadic bind semantics (same as `Task.bind`); the previous signature made the function effectively equivalent to direct application
1.1.1
- perf: use while! in groupBy, countBy, partition, except, exceptOfSeq to eliminate redundant mutable 'go' variables and initial MoveNextAsync calls
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 4fcf36c..0284354 100644
--- a/src/FSharp.Control.TaskSeq.Test/FSharp.Control.TaskSeq.Test.fsproj
+++ b/src/FSharp.Control.TaskSeq.Test/FSharp.Control.TaskSeq.Test.fsproj
@@ -80,6 +80,7 @@
+
diff --git a/src/FSharp.Control.TaskSeq.Test/Utils.Tests.fs b/src/FSharp.Control.TaskSeq.Test/Utils.Tests.fs
new file mode 100644
index 0000000..c89765c
--- /dev/null
+++ b/src/FSharp.Control.TaskSeq.Test/Utils.Tests.fs
@@ -0,0 +1,116 @@
+module TaskSeq.Tests.Utils
+
+open System
+open System.Threading.Tasks
+open Xunit
+open FsUnit.Xunit
+
+open FSharp.Control
+
+
+module AsyncBind =
+ []
+ let ``Async.bind awaits the async and passes the value to the binder`` () =
+ let result =
+ async { return 21 }
+ |> Async.bind (fun n -> async { return n * 2 })
+ |> Async.RunSynchronously
+
+ result |> should equal 42
+
+ []
+ let ``Async.bind propagates exceptions from the source async`` () =
+ let run () =
+ async { return raise (InvalidOperationException "source error") }
+ |> Async.bind (fun (_: int) -> async { return 0 })
+ |> Async.RunSynchronously
+
+ (fun () -> run () |> ignore)
+ |> should throw typeof
+
+ []
+ let ``Async.bind propagates exceptions from the binder`` () =
+ let run () =
+ async { return 1 }
+ |> Async.bind (fun _ -> async { return raise (InvalidOperationException "binder error") })
+ |> Async.RunSynchronously
+
+ (fun () -> run () |> ignore)
+ |> should throw typeof
+
+ []
+ let ``Async.bind chains correctly`` () =
+ let result =
+ async { return 1 }
+ |> Async.bind (fun n -> async { return n + 10 })
+ |> Async.bind (fun n -> async { return n + 100 })
+ |> Async.RunSynchronously
+
+ result |> should equal 111
+
+ []
+ let ``Async.bind passes the unwrapped value, not the Async wrapper`` () =
+ // This test specifically verifies the bug fix: binder receives 'T, not Async<'T>
+ let mutable receivedType = typeof
+
+ async { return 42 }
+ |> Async.bind (fun (n: int) ->
+ receivedType <- n.GetType()
+ async { return () })
+ |> Async.RunSynchronously
+
+ receivedType |> should equal typeof
+
+
+module TaskBind =
+ []
+ let ``Task.bind awaits the task and passes the value to the binder`` () = task {
+ let result =
+ task { return 21 }
+ |> Task.bind (fun n -> task { return n * 2 })
+
+ let! v = result
+ v |> should equal 42
+ }
+
+ []
+ let ``Task.bind chains correctly`` () = task {
+ let result =
+ task { return 1 }
+ |> Task.bind (fun n -> task { return n + 10 })
+ |> Task.bind (fun n -> task { return n + 100 })
+
+ let! v = result
+ v |> should equal 111
+ }
+
+
+module AsyncMap =
+ []
+ let ``Async.map transforms the result`` () =
+ let result =
+ async { return 21 }
+ |> Async.map (fun n -> n * 2)
+ |> Async.RunSynchronously
+
+ result |> should equal 42
+
+ []
+ let ``Async.map chains correctly`` () =
+ let result =
+ async { return 1 }
+ |> Async.map (fun n -> n + 10)
+ |> Async.map (fun n -> n + 100)
+ |> Async.RunSynchronously
+
+ result |> should equal 111
+
+
+module TaskMap =
+ []
+ let ``Task.map transforms the result`` () = task {
+ let result = task { return 21 } |> Task.map (fun n -> n * 2)
+
+ let! v = result
+ v |> should equal 42
+ }
diff --git a/src/FSharp.Control.TaskSeq/CompatibilitySuppressions.xml b/src/FSharp.Control.TaskSeq/CompatibilitySuppressions.xml
index 488c87a..c0e8a35 100644
--- a/src/FSharp.Control.TaskSeq/CompatibilitySuppressions.xml
+++ b/src/FSharp.Control.TaskSeq/CompatibilitySuppressions.xml
@@ -1,6 +1,13 @@
+
+ CP0002
+ M:FSharp.Control.Async.bind``2(Microsoft.FSharp.Core.FSharpFunc{Microsoft.FSharp.Control.FSharpAsync{``0},Microsoft.FSharp.Control.FSharpAsync{``1}},Microsoft.FSharp.Control.FSharpAsync{``0})
+ lib/netstandard2.1/FSharp.Control.TaskSeq.dll
+ lib/netstandard2.1/FSharp.Control.TaskSeq.dll
+ true
+
CP0002
M:FSharp.Control.LowPriority.TaskSeqBuilder#Bind``5(FSharp.Control.TaskSeqBuilder,``0,Microsoft.FSharp.Core.FSharpFunc{``1,Microsoft.FSharp.Core.CompilerServices.ResumableCode{FSharp.Control.TaskSeqStateMachineData{``2},Microsoft.FSharp.Core.Unit}})
diff --git a/src/FSharp.Control.TaskSeq/Utils.fs b/src/FSharp.Control.TaskSeq/Utils.fs
index ec076f2..147734b 100644
--- a/src/FSharp.Control.TaskSeq/Utils.fs
+++ b/src/FSharp.Control.TaskSeq/Utils.fs
@@ -72,4 +72,7 @@ module Async =
return mapper result
}
- let inline bind binder (async: Async<'T>) : Async<'U> = ExtraTopLevelOperators.async { return! binder async }
+ let inline bind binder (async: Async<'T>) : Async<'U> = ExtraTopLevelOperators.async {
+ let! result = async
+ return! binder result
+ }
diff --git a/src/FSharp.Control.TaskSeq/Utils.fsi b/src/FSharp.Control.TaskSeq/Utils.fsi
index b171720..d252d45 100644
--- a/src/FSharp.Control.TaskSeq/Utils.fsi
+++ b/src/FSharp.Control.TaskSeq/Utils.fsi
@@ -103,4 +103,4 @@ module Async =
val inline map: mapper: ('T -> 'U) -> async: Async<'T> -> Async<'U>
/// Bind an Async<'T>
- val inline bind: binder: (Async<'T> -> Async<'U>) -> async: Async<'T> -> Async<'U>
+ val inline bind: binder: ('T -> Async<'U>) -> async: Async<'T> -> Async<'U>