Occasionally, I want to return a mutable collection from a function as a sequence. Upcasting to seq<_> works, but the sequence could be downcasted and modified (not that it usually matters). My usual solution is to use a wrap-as-a-sequence function which has given rise to the following:
let wrap items = Seq.map id
let wrapDict dict = Seq.map ((|KeyValue|) >> snd)
Mostly for curiosity (and fun), what would be other ways of writing such functions, perhaps in a more idiomatic, concise, or performant way?
Seq.readonly is the function you are looking for.
I think that the function suggested by Stephan is probably the thing you're looking for.
However, there is one tricky thing - the Seq.readonly function (as well as ReadOnlyCollection or using Seq.map) wraps the sequence so that it cannot be mutated from the outside. However, it still has quite subtle behavior because the resulting sequence can be mutated by the object:
type Arr() =
let data = [| 1 .. 5 |]
member x.ItemsSeq = Seq.readonly data
member x.Mutate() = data.[0] <- 10
let a = Arr()
let s = a.ItemsSeq
printfn "%A" (s |> List.ofSeq) // [1; 2; 3; 4; 5]
a.Mutate()
printfn "%A" (s |> List.ofSeq) // [10; 2; 3; 4; 5]
I would think that this is quite unexpected behavior (in a mostly functional language).
So, if the internal collection is mutable, you may also consider creating a complete clone of the data that cannot be modified later and guarantees that the returned sequence will always give the same results. For example using data |> Array.ofSeq |> Seq.readonly
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With