Skip to content

Commit 9d29d7e

Browse files
authored
Merge pull request #202 from njlr/housekeeping/issue-38-remove-ce-return
housekeeping/issue-38-remove-ce-return
2 parents 4bdcf15 + 848f6b2 commit 9d29d7e

File tree

2 files changed

+43
-25
lines changed

2 files changed

+43
-25
lines changed

src/FSharp.Control.AsyncSeq/AsyncSeq.fs

Lines changed: 18 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -297,20 +297,20 @@ module AsyncSeqOp =
297297
type OptimizedUnfoldEnumerator<'S, 'T> (f:'S -> Async<('T * 'S) option>, init:'S) =
298298
let mutable currentState = init
299299
let mutable disposed = false
300-
300+
301301
interface IAsyncEnumerator<'T> with
302-
member __.MoveNext () : Async<'T option> =
302+
member __.MoveNext () : Async<'T option> =
303303
if disposed then async.Return None
304304
else async {
305305
let! result = f currentState
306306
match result with
307-
| None ->
307+
| None ->
308308
return None
309309
| Some (value, nextState) ->
310310
currentState <- nextState
311311
return Some value
312312
}
313-
member __.Dispose () =
313+
member __.Dispose () =
314314
disposed <- true
315315

316316
type UnfoldAsyncEnumerator<'S, 'T> (f:'S -> Async<('T * 'S) option>, init:'S) =
@@ -458,13 +458,6 @@ module AsyncSeq =
458458
type AsyncSeqBuilder() =
459459
member x.Yield(v) =
460460
singleton v
461-
// This looks weird, but it is needed to allow:
462-
//
463-
// while foo do
464-
// do! something
465-
//
466-
// because F# translates body as Bind(something, fun () -> Return())
467-
member x.Return () = empty
468461
member x.YieldFrom(s:AsyncSeq<'T>) =
469462
s
470463
member x.Zero () = empty
@@ -606,10 +599,10 @@ module AsyncSeq =
606599
// Optimized collect implementation using direct field access instead of ref cells
607600
type OptimizedCollectEnumerator<'T, 'U>(f: 'T -> AsyncSeq<'U>, inp: AsyncSeq<'T>) =
608601
// Mutable fields instead of ref cells to reduce allocations
609-
let mutable inputEnumerator: IAsyncEnumerator<'T> option = None
602+
let mutable inputEnumerator: IAsyncEnumerator<'T> option = None
610603
let mutable innerEnumerator: IAsyncEnumerator<'U> option = None
611604
let mutable disposed = false
612-
605+
613606
// Tail-recursive optimization to avoid deep continuation chains
614607
let rec moveNextLoop () : Async<'U option> = async {
615608
if disposed then return None
@@ -642,7 +635,7 @@ module AsyncSeq =
642635
inputEnumerator <- Some newOuter
643636
return! moveNextLoop ()
644637
}
645-
638+
646639
interface IAsyncEnumerator<'U> with
647640
member _.MoveNext() = moveNextLoop ()
648641
member _.Dispose() =
@@ -651,13 +644,13 @@ module AsyncSeq =
651644
match innerEnumerator with
652645
| Some inner -> inner.Dispose(); innerEnumerator <- None
653646
| None -> ()
654-
match inputEnumerator with
647+
match inputEnumerator with
655648
| Some outer -> outer.Dispose(); inputEnumerator <- None
656649
| None -> ()
657650

658651
let collect (f: 'T -> AsyncSeq<'U>) (inp: AsyncSeq<'T>) : AsyncSeq<'U> =
659652
{ new IAsyncEnumerable<'U> with
660-
member _.GetEnumerator() =
653+
member _.GetEnumerator() =
661654
new OptimizedCollectEnumerator<'T, 'U>(f, inp) :> IAsyncEnumerator<'U> }
662655

663656
// let collect (f: 'T -> AsyncSeq<'U>) (inp: AsyncSeq<'T>) : AsyncSeq<'U> =
@@ -749,7 +742,7 @@ module AsyncSeq =
749742
// Optimized iterAsync implementation to reduce allocations
750743
type internal OptimizedIterAsyncEnumerator<'T>(enumerator: IAsyncEnumerator<'T>, f: 'T -> Async<unit>) =
751744
let mutable disposed = false
752-
745+
753746
member _.IterateAsync() =
754747
let rec loop() = async {
755748
let! next = enumerator.MoveNext()
@@ -760,17 +753,17 @@ module AsyncSeq =
760753
| None -> return ()
761754
}
762755
loop()
763-
756+
764757
interface IDisposable with
765758
member _.Dispose() =
766759
if not disposed then
767760
disposed <- true
768761
enumerator.Dispose()
769762

770-
// Optimized iteriAsync implementation with direct tail recursion
763+
// Optimized iteriAsync implementation with direct tail recursion
771764
type internal OptimizedIteriAsyncEnumerator<'T>(enumerator: IAsyncEnumerator<'T>, f: int -> 'T -> Async<unit>) =
772765
let mutable disposed = false
773-
766+
774767
member _.IterateAsync() =
775768
let rec loop count = async {
776769
let! next = enumerator.MoveNext()
@@ -781,7 +774,7 @@ module AsyncSeq =
781774
| None -> return ()
782775
}
783776
loop 0
784-
777+
785778
interface IDisposable with
786779
member _.Dispose() =
787780
if not disposed then
@@ -798,7 +791,7 @@ module AsyncSeq =
798791
let iterAsync (f: 'T -> Async<unit>) (source: AsyncSeq<'T>) =
799792
match source with
800793
| :? AsyncSeqOp<'T> as source -> source.IterAsync f
801-
| _ ->
794+
| _ ->
802795
async {
803796
let enum = source.GetEnumerator()
804797
use optimizer = new OptimizedIterAsyncEnumerator<_>(enum, f)
@@ -864,7 +857,7 @@ module AsyncSeq =
864857
// Optimized mapAsync enumerator that avoids computation builder overhead
865858
type private OptimizedMapAsyncEnumerator<'T, 'TResult>(source: IAsyncEnumerator<'T>, f: 'T -> Async<'TResult>) =
866859
let mutable disposed = false
867-
860+
868861
interface IAsyncEnumerator<'TResult> with
869862
member _.MoveNext() = async {
870863
let! moveResult = source.MoveNext()
@@ -874,7 +867,7 @@ module AsyncSeq =
874867
let! mapped = f value
875868
return Some mapped
876869
}
877-
870+
878871
member _.Dispose() =
879872
if not disposed then
880873
disposed <- true
@@ -885,7 +878,7 @@ module AsyncSeq =
885878
| :? AsyncSeqOp<'T> as source -> source.MapAsync f
886879
| _ ->
887880
{ new IAsyncEnumerable<'TResult> with
888-
member _.GetEnumerator() =
881+
member _.GetEnumerator() =
889882
new OptimizedMapAsyncEnumerator<'T, 'TResult>(source.GetEnumerator(), f) :> IAsyncEnumerator<'TResult> }
890883

891884
let mapiAsync f (source : AsyncSeq<'T>) : AsyncSeq<'TResult> = asyncSeq {

tests/FSharp.Control.AsyncSeq.Tests/AsyncSeqTests.fs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -581,6 +581,31 @@ let ``AsyncSeq.bufferByTimeAndCount empty``() =
581581
//
582582
// Assert.True ((actual = expected))
583583

584+
[<Test>]
585+
let ``AsyncSeq.while do CE is possible`` () =
586+
let mutable i = 0
587+
let mutable foo = true
588+
let something =
589+
async {
590+
i <- i + 1
591+
foo <- i < 3
592+
do! Async.Sleep 10
593+
}
594+
let actual =
595+
asyncSeq {
596+
yield "a"
597+
598+
while foo do
599+
do! something
600+
601+
yield "b"
602+
yield "c"
603+
}
604+
|> AsyncSeq.toListAsync
605+
|> Async.RunSynchronously
606+
607+
Assert.AreEqual([ "a"; "b"; "c" ], actual)
608+
584609
[<Test>]
585610
let ``AsyncSeq.bufferByCountAndTime should not block`` () =
586611
let op =

0 commit comments

Comments
 (0)