Seq.iter fails to iterate the sequence created with seq.collect and sequence expression

Viewed 84

I've had a bug in F# code that I have reduced to the following minimal reproduction sequence, but now I don't understand why it works that way.

let duplicate element =
    [ element; element ]

let passThrough (sq: seq<_>) =
    use it = sq.GetEnumerator ()
    seq {
        while (it.MoveNext ()) do
            yield it.Current
    }

[<EntryPoint>]
let main _ =
    [0; 1]
    |> Seq.collect (duplicate)
    (* |> Seq.toArray // When uncommented - works as expected. *)
    |> passThrough
    |> Seq.iter (fun i -> printf $"{i} ")
    0

When the Seq.toArray call is uncommented, it produces the result I expect, i.e. iterates the sequence pipeline and prints 0 0 1 1. However with that line commented out, the code just finishes without printing anything.

1 Answers

I conferred with one of our experts at F# Slack (thanks R. C.), and was advised that a proper implementation of passThrough should look like this. The enumerator is then properly disposed when the while loop exits. The problem with the original implementation is that the enumerator is disposed at a point in time before the while loop exits, if ever.

let passThrough (sq: seq<_>) =
    seq {
        use it = sq.GetEnumerator ()
        while (it.MoveNext ()) do
            yield it.Current
    }
Related