复制递归F#记录类型

pla*_*ist 5 recursion f# record

假设你有以下递归记录类型

type Parent = {
    Name : string
    Age : int
    Children : Child list }
and Child = {
    Name : string
    Parent : Parent option }
Run Code Online (Sandbox Code Playgroud)

我可以轻松地创建实例

module Builder = 
    let create name kids =
        let rec makeChild kid = { kid with Parent = parent |> Some }
        and parent = 
            {
                Name = name
                Age = 42
                Children = children
            }
        and children = kids |> List.map makeChild

        parent

    let createChild name =
        { Child.Name = name; Parent = None }
Run Code Online (Sandbox Code Playgroud)

但当我尝试使用"with"将现有成人"转化"为父母时:

module Builder2 = 
    let createAdult name age = 
        { Parent.Name = name; Age = age; Children = [] }

    let create name kids =
        let rec makeChild kid = { kid with Parent = parent |> Some }
        and parent = 
            { (createAdult name 42) with
                Children = children
            }
        and children = kids |> List.map makeChild

        parent

    let createChild name =
        { Child.Name = name; Parent = None }
Run Code Online (Sandbox Code Playgroud)

我明白了:

错误FS0040:将通过使用延迟引用在运行时检查对定义的对象的此递归引用和其他递归引用的初始化 - 健全性.这是因为您定义了一个或多个递归对象,而不是递归函数.使用'#nowarn"40"'或'--nowarn:40'可以抑制此警告.

并突出显示"父"定义中的"Children = children".

我究竟做错了什么?

编辑:

还有一点:当我将"Builder"(有效)移动到另一个组件(例如测试组件)时,它立即停止工作:

错误FS0261:递归值不能直接分配给递归绑定中"父"类型的非可变字段"Children".考虑使用可变字段.

编辑: 基于我试过的评论

let create name kids =
    let rec makeChild kid = { kid with Parent = parent |> Some }
    and adult = createAdult name 42
    and parent = 
        { adult with Children = children }
    and children = kids |> List.map makeChild
Run Code Online (Sandbox Code Playgroud)

但仍然没有运气 - 编译器仍然没有看到这个用例与工作类似:(

pla*_*ist 0

cartermp 在这里找到并发布了解决方案:

https://github.com/Microsoft/visualfsharp/issues/4201

我在这里发布了一个复制品

https://github.com/plainionist/DevNull/tree/master/src/FSharpCopyRecordRecursive

当然,所提出的解决方案很有魅力