From da20f097d1c840c125b54081efa6f58c44fc4a9f Mon Sep 17 00:00:00 2001 From: Repo Assist Date: Sun, 22 Feb 2026 13:38:13 +0000 Subject: [PATCH] Add CompilerMessage warning to groupBy and groupByAsync for parallel consumption requirement Adds FS9999 warning to both groupBy and groupByAsync to alert callers that the resulting sequence must be consumed with a parallel combinator (e.g. AsyncSeq.mapAsyncParallel) to avoid deadlocks. Closes #125 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/FSharp.Control.AsyncSeq/AsyncSeq.fs | 2 ++ src/FSharp.Control.AsyncSeq/AsyncSeq.fsi | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/FSharp.Control.AsyncSeq/AsyncSeq.fs b/src/FSharp.Control.AsyncSeq/AsyncSeq.fs index 47d91fa..9f609d1 100644 --- a/src/FSharp.Control.AsyncSeq/AsyncSeq.fs +++ b/src/FSharp.Control.AsyncSeq/AsyncSeq.fs @@ -1983,6 +1983,7 @@ module AsyncSeq = toAsyncSeqImpl s.tail.Value + [] let groupByAsync (p:'a -> Async<'k>) (s:AsyncSeq<'a>) : AsyncSeq<'k * AsyncSeq<'a>> = asyncSeq { let groups = Dictionary<'k, AsyncSeqSrc< 'a>>() let close group = @@ -2014,6 +2015,7 @@ module AsyncSeq = raise ex } yield! go () } + [] let groupBy (p:'a -> 'k) (s:AsyncSeq<'a>) : AsyncSeq<'k * AsyncSeq<'a>> = groupByAsync (p >> async.Return) s #endif diff --git a/src/FSharp.Control.AsyncSeq/AsyncSeq.fsi b/src/FSharp.Control.AsyncSeq/AsyncSeq.fsi index 2d3309a..5d1554f 100644 --- a/src/FSharp.Control.AsyncSeq/AsyncSeq.fsi +++ b/src/FSharp.Control.AsyncSeq/AsyncSeq.fsi @@ -562,6 +562,7 @@ module AsyncSeq = /// /// Note that the resulting async sequence has to be processed in parallel (e.g AsyncSeq.mapAsyncParallel) becaused /// completion of sub-sequences depends on completion of other sub-sequences. + [] val groupByAsync<'T, 'Key when 'Key : equality> : projection:('T -> Async<'Key>) -> source:AsyncSeq<'T> -> AsyncSeq<'Key * AsyncSeq<'T>> /// Applies a key-generating function to each element and returns an async sequence containing unique keys @@ -569,6 +570,7 @@ module AsyncSeq = /// /// Note that the resulting async sequence has to be processed in parallel (e.g AsyncSeq.mapAsyncParallel) becaused /// completion of sub-sequences depends on completion of other sub-sequences. + [] val groupBy<'T, 'Key when 'Key : equality> : projection:('T -> 'Key) -> source:AsyncSeq<'T> -> AsyncSeq<'Key * AsyncSeq<'T>> #if (NETSTANDARD || NET)