I have the following code:
open System
open System.Linq
type Child = {
id: Guid
name: int
parent: Guid
}
type Parent = {
id: Guid
name: int
children: seq<Guid>
}
let makeChild name parentId =
{
Child.id = Guid.NewGuid()
name = name
parent = parentId
}
let makeParent (group: IGrouping<int, int>) =
let id = Guid.NewGuid()
let children = group |> Seq.map (fun x -> makeChild x id)
let ids = children |> Seq.map (fun x -> x.id)
({
Parent.id = id
name = group.Key
children = ids
}, children)
let makeAll (groups: seq<IGrouping<int, int>>) =
let result = groups |> Seq.map (fun x -> makeParent x)
let children = result |> Seq.map (fun x -> snd x) |> Seq.concat
let parents = result |> Seq.map (fun x -> fst x)
(parents, children)
(I accept IGrouping<int, int>
instead of seq<int * seq<int>>
because this code needs to interoperate with C#.)
However, when I run with the following:
let parents, children = makeAll(Enumerable.Range(0, 100).GroupBy(fun x -> x % 10))
then none of the children.[i].parent guids correlate with the parents.[j].children.[k] guids for i, j, k.
Why is this not the case? How can I get it to be so?
Didn't test that, but it seems the problem is in the fact you enumerate the result seq twice, once in the let children
, once in the let parents
line. And since guid generation is side-effecting, you get two different results for each of the enumerations.
If you cache the seq in the let result
line (or materialize it by turning it into an array or a list in the same line), you should get what you're looking for:
let result = groups |> Seq.map (fun x -> makeParent x) |> Seq.cache
The same in the makeParent
function. The ids
seq needs to be cached as well.
"Traps" like this are why I find it preferable to use concrete collection types rather than seqs on the boundaries of functions or interfaces. And if you're looking for laziness, you can make it explicit by using Lazy type.
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