Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Guid generation in F# sequences

Tags:

f#

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?

like image 641
cm007 Avatar asked Sep 17 '25 01:09

cm007


1 Answers

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.

like image 72
scrwtp Avatar answered Sep 19 '25 13:09

scrwtp