Skip to content

Commit a9c7e56

Browse files
authored
Merge branch 'main' into repo-assist/eng-update-fsdocs-22alpha3-20260403-596ea5adde75c6f0
2 parents 36f75bc + 7e64987 commit a9c7e56

File tree

10 files changed

+802
-428
lines changed

10 files changed

+802
-428
lines changed

.config/dotnet-tools.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
"rollForward": false
1111
},
1212
"fable": {
13-
"version": "4.25.0",
13+
"version": "5.0.0-rc.7",
1414
"commands": [
1515
"fable"
1616
],

.github/aw/actions-lock.json

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,20 @@
55
"version": "v6.0.2",
66
"sha": "de0fac2e4500dabe0009e67214ff5f5447ce83dd"
77
},
8-
"actions/github-script@v8": {
8+
"actions/github-script@v9.0.0": {
99
"repo": "actions/github-script",
10-
"version": "v8",
11-
"sha": "ed597411d8f924073f98dfc5c65a23a2325f34cd"
10+
"version": "v9.0.0",
11+
"sha": "d746ffe35508b1917358783b479e04febd2b8f71"
1212
},
13-
"github/gh-aw-actions/setup@v0.65.6": {
13+
"github/gh-aw-actions/setup@v0.68.1": {
1414
"repo": "github/gh-aw-actions/setup",
15-
"version": "v0.65.6",
16-
"sha": "31130b20a8fd3ef263acbe2091267c0aace07e09"
15+
"version": "v0.68.1",
16+
"sha": "2fe53acc038ba01c3bbdc767d4b25df31ca5bdfc"
1717
},
18-
"github/gh-aw/actions/setup@v0.65.6": {
18+
"github/gh-aw/actions/setup@v0.68.1": {
1919
"repo": "github/gh-aw/actions/setup",
20-
"version": "v0.65.6",
21-
"sha": "296262211b372a13b451ca9400369ac152db75d6"
20+
"version": "v0.68.1",
21+
"sha": "5a06d310cf45161bde77d070065a1e1489fc411c"
2222
}
2323
}
2424
}

.github/workflows/repo-assist.lock.yml

Lines changed: 499 additions & 394 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.github/workflows/repo-assist.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ steps:
176176
json.dump(result, f, indent=2)
177177
EOF
178178
179-
source: githubnext/agentics/workflows/repo-assist.md@4ea8c81959909f40373e2a5c2b7fdb54ea19e0a5
179+
source: githubnext/agentics/workflows/repo-assist.md@97143ac59cb3a13ef2a77581f929f06719c7402a
180180
---
181181

182182
# Repo Assist

RELEASE_NOTES.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,16 @@
1+
### 4.13.0
2+
3+
* CI: Upgrade Fable from 4.25.0 to 5.0.0-rc.7 and .NET SDK from 8.0.19 to 10.0.100 to fix a CI hang where the Fable build step ran for 6+ hours with .NET 10. Fable 5 + .NET 10 compiles in ~20 seconds.
4+
* CI: Conditionally disable SourceLink for Fable builds to fix a compilation error where AssemblyInfo.fs was being embedded by SourceLink and passed to the Fable compiler.
5+
* Tests: Update 4 Fable tests that used `rejects.toThrow()` to use `Async.Catch` with a `Choice2Of2` pattern match instead, since Fable 5's `failwith` throws a custom `Exception` type that is not a JavaScript `Error` instance.
6+
* Added `AsyncSeq.ofList` — creates an async sequence from an F# list with an optimised direct-enumerator implementation (avoids `IEnumerator<T>` boxing).
7+
* Added `AsyncSeq.ofArray` — creates an async sequence from an array with an optimised index-based enumerator (avoids `IEnumerator<T>` boxing).
8+
* Added `AsyncSeq.cycle` — infinitely cycles through all elements of a source async sequence; returns empty if the source is empty.
9+
110
### 4.12.0
211

12+
* Tests: Added tests for `mapiAsync`, `tryPickAsync`, `pickAsync`, and `groupByAsync` — these four async functions previously had no test coverage.
13+
* Performance: Modernised `singleton`, `collectSeq`, `takeWhileInclusive`, and `takeWhileInclusiveAsync` to use `mutable` local variables instead of `ref` cells (`!`/`:=` operators). Eliminates heap-allocated `Ref<T>` objects in these enumerators, reducing GC pressure consistent with the improvements in 4.11.0.
314
* Added `AsyncSeq.tryFindBack` — returns the last element for which the predicate returns true, or `None` if no match. Mirrors `Array.tryFindBack` / `List.tryFindBack`.
415
* Added `AsyncSeq.tryFindBackAsync` — async-predicate variant of `tryFindBack`.
516
* Added `AsyncSeq.findBack` — returns the last element for which the predicate returns true; raises `KeyNotFoundException` if no match. Mirrors `Array.findBack` / `List.findBack`.

src/FSharp.Control.AsyncSeq/AsyncSeq.fs

Lines changed: 55 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -450,11 +450,11 @@ module AsyncSeq =
450450

451451
let singleton (v:'T) : AsyncSeq<'T> =
452452
AsyncSeqImpl(fun () ->
453-
let state = ref 0
453+
let mutable state = 0
454454
{ new IAsyncSeqEnumerator<'T> with
455455
member _.MoveNext() = async {
456-
let res = state.Value = 0
457-
incr state
456+
let res = state = 0
457+
state <- state + 1
458458
return (if res then Some v else None) }
459459
interface System.IDisposable with
460460
member _.Dispose() = () }) :> AsyncSeq<'T>
@@ -719,20 +719,20 @@ module AsyncSeq =
719719
// Like collect, but the input is a sequence, where no bind is required on each step of the enumeration
720720
let collectSeq (f: 'T -> AsyncSeq<'U>) (inp: seq<'T>) : AsyncSeq<'U> =
721721
AsyncSeqImpl(fun () ->
722-
let state = ref (CollectSeqState.NotStarted inp)
722+
let mutable state = CollectSeqState.NotStarted inp
723723
{ new IAsyncSeqEnumerator<'U> with
724724
member x.MoveNext() =
725-
async { match !state with
725+
async { match state with
726726
| CollectSeqState.NotStarted inp ->
727727
return!
728728
(let e1 = inp.GetEnumerator()
729-
state := CollectSeqState.HaveInputEnumerator e1
729+
state <- CollectSeqState.HaveInputEnumerator e1
730730
x.MoveNext())
731731
| CollectSeqState.HaveInputEnumerator e1 ->
732732
return!
733733
(if e1.MoveNext() then
734734
let e2 = (f e1.Current).GetEnumerator()
735-
state := CollectSeqState.HaveInnerEnumerator (e1, e2)
735+
state <- CollectSeqState.HaveInnerEnumerator (e1, e2)
736736
else
737737
x.Dispose()
738738
x.MoveNext())
@@ -741,19 +741,19 @@ module AsyncSeq =
741741
match res2 with
742742
| None ->
743743
return!
744-
(state := CollectSeqState.HaveInputEnumerator e1
744+
(state <- CollectSeqState.HaveInputEnumerator e1
745745
dispose e2
746746
x.MoveNext())
747747
| Some _ ->
748748
return res2
749749
| _ -> return None}
750750
member x.Dispose() =
751-
match !state with
751+
match state with
752752
| CollectSeqState.HaveInputEnumerator e1 ->
753-
state := CollectSeqState.Finished
753+
state <- CollectSeqState.Finished
754754
dispose e1
755755
| CollectSeqState.HaveInnerEnumerator (e1, e2) ->
756-
state := CollectSeqState.Finished
756+
state <- CollectSeqState.Finished
757757
dispose e2
758758
dispose e1
759759
x.Dispose()
@@ -790,6 +790,35 @@ module AsyncSeq =
790790
dispose e
791791
| _ -> () }) :> AsyncSeq<'T>
792792

793+
let ofList (source: 'T list) : AsyncSeq<'T> =
794+
AsyncSeqImpl(fun () ->
795+
let mutable remaining = source
796+
{ new IAsyncSeqEnumerator<'T> with
797+
member _.MoveNext() =
798+
async {
799+
match remaining with
800+
| [] -> return None
801+
| h :: t ->
802+
remaining <- t
803+
return Some h
804+
}
805+
member _.Dispose() = () }) :> AsyncSeq<'T>
806+
807+
let ofArray (source: 'T []) : AsyncSeq<'T> =
808+
AsyncSeqImpl(fun () ->
809+
let mutable i = 0
810+
{ new IAsyncSeqEnumerator<'T> with
811+
member _.MoveNext() =
812+
async {
813+
if i < source.Length then
814+
let v = source.[i]
815+
i <- i + 1
816+
return Some v
817+
else
818+
return None
819+
}
820+
member _.Dispose() = () }) :> AsyncSeq<'T>
821+
793822
let appendSeq (seq2: seq<'T>) (source: AsyncSeq<'T>) : AsyncSeq<'T> =
794823
append source (ofSeq seq2)
795824

@@ -1982,35 +2011,35 @@ module AsyncSeq =
19822011
let takeWhileInclusive (f : 'a -> bool) (s : AsyncSeq<'a>) : AsyncSeq<'a> =
19832012
AsyncSeqImpl(fun () ->
19842013
let en = s.GetEnumerator()
1985-
let fin = ref false
2014+
let mutable fin = false
19862015
{ new IAsyncSeqEnumerator<'a> with
19872016
member _.MoveNext() = async {
1988-
if !fin then return None
2017+
if fin then return None
19892018
else
19902019
let! next = en.MoveNext()
19912020
match next with
19922021
| None -> return None
19932022
| Some a ->
19942023
if f a then return Some a
1995-
else fin := true; return Some a }
2024+
else fin <- true; return Some a }
19962025
interface System.IDisposable with
19972026
member _.Dispose() = en.Dispose() }) :> AsyncSeq<'a>
19982027

19992028
let takeWhileInclusiveAsync (predicate: 'T -> Async<bool>) (source: AsyncSeq<'T>) : AsyncSeq<'T> =
20002029
AsyncSeqImpl(fun () ->
20012030
let en = source.GetEnumerator()
2002-
let fin = ref false
2031+
let mutable fin = false
20032032
{ new IAsyncSeqEnumerator<'T> with
20042033
member _.MoveNext() = async {
2005-
if !fin then return None
2034+
if fin then return None
20062035
else
20072036
let! next = en.MoveNext()
20082037
match next with
20092038
| None -> return None
20102039
| Some a ->
20112040
let! ok = predicate a
20122041
if ok then return Some a
2013-
else fin := true; return Some a }
2042+
else fin <- true; return Some a }
20142043
interface System.IDisposable with
20152044
member _.Dispose() = en.Dispose() }) :> AsyncSeq<'T>
20162045

@@ -2160,6 +2189,15 @@ module AsyncSeq =
21602189
let toArraySynchronously (source:AsyncSeq<'T>) = toArrayAsync source |> Async.RunSynchronously
21612190
#endif
21622191

2192+
let cycle (source: AsyncSeq<'T>) : AsyncSeq<'T> =
2193+
asyncSeq {
2194+
let! arr = source |> toArrayAsync
2195+
if arr.Length > 0 then
2196+
while true do
2197+
for x in arr do
2198+
yield x
2199+
}
2200+
21632201
let partitionAsync (predicate: 'T -> Async<bool>) (source: AsyncSeq<'T>) : Async<'T[] * 'T[]> = async {
21642202
let trues = ResizeArray<'T>()
21652203
let falses = ResizeArray<'T>()

src/FSharp.Control.AsyncSeq/AsyncSeq.fsi

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,11 @@ module AsyncSeq =
6060
/// Creates an async sequence given by evaluating the specified async computation until it returns None.
6161
val replicateUntilNoneAsync : Async<'T option> -> AsyncSeq<'T>
6262

63+
/// Returns an async sequence which infinitely cycles through all elements of the source sequence.
64+
/// The source is materialised into an array on first enumeration. Returns an empty sequence if
65+
/// the source is empty.
66+
val cycle : source:AsyncSeq<'T> -> AsyncSeq<'T>
67+
6368
/// Returns an async sequence which emits an element on a specified period.
6469
val intervalMs : periodMs:int -> AsyncSeq<DateTime>
6570

@@ -488,6 +493,14 @@ module AsyncSeq =
488493
/// input synchronous sequence and returns them one-by-one.
489494
val ofSeq : source:seq<'T> -> AsyncSeq<'T>
490495

496+
/// Creates an asynchronous sequence that lazily takes elements from an
497+
/// F# list and returns them one-by-one.
498+
val ofList : source:'T list -> AsyncSeq<'T>
499+
500+
/// Creates an asynchronous sequence that lazily takes elements from an
501+
/// array and returns them one-by-one.
502+
val ofArray : source:'T [] -> AsyncSeq<'T>
503+
491504
/// Creates an asynchronous sequence that lazily takes element from an
492505
/// input synchronous sequence of asynchronous computation and returns them one-by-one.
493506
val ofSeqAsync : seq<Async<'T>> -> AsyncSeq<'T>

src/FSharp.Control.AsyncSeq/FSharp.Control.AsyncSeq.fsproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
1717
<RepositoryType>git</RepositoryType>
1818
<GenerateDocumentationFile>true</GenerateDocumentationFile>
19-
<EnableSourceLink>true</EnableSourceLink>
19+
<EnableSourceLink Condition="'$(FABLE_COMPILER)' != 'True'">true</EnableSourceLink>
2020
</PropertyGroup>
2121
<ItemGroup>
2222
<Compile Include="AsyncSeq.fsi" />

0 commit comments

Comments
 (0)