下面的代码导致编译器错误,"FS0695:此递归绑定使用了递归形式的无效混合。"
type Parent =
{ ParentName : string
Children : Child list }
and Child =
{ ChildName : string
Parent : Parent }
let createChild parent childName =
{ ChildName = childName
Parent = parent }
let createParent parentName childNames =
let rec children = childNames |> List.map (fun childName -> createChild parent childName)
and parent = {
ParentName = parentName
Children = children
}
parent
错误信息是什么意思?我应该如何纠正这个代码?
这段代码的目的是用循环引用初始化f#不可变记录。我试着到处应用这些方法,但都没有成功。
我不熟悉这个错误,它似乎没有被记录在任何地方,但这是我可以在玩了它一点后推测的。
问题似乎在于parent
如何被lambda表达式关闭,该表达式本身位于children
的定义内。这显然会抛出类型检查器(tc.fs)—我的猜测是因为children
绑定本身的右侧包含一个表达式(lambda表达式),该表达式本身包含一个递归引用,但真正的原因可能比这更微妙。注意,如果像这样反转声明:
let createParent parentName childNames =
let rec parent = {
ParentName = parentName
Children = children }
and children = childNames |> List.map (fun childName -> createChild parent childName)
parent
您还将在绑定children
的行上看到此警告:
警告:将在运行时通过使用延迟引用来检查对所定义对象的此和其他递归引用的初始化合理性。这是因为您正在定义一个或多个递归对象,而不是递归函数。
要解决这个问题,似乎可以将闭包拉出一级:
let createParent parentName childNames =
let rec makeChild n = createChild parent n
and parent = {
ParentName = parentName
Children = children }
and children = childNames |> List.map makeChild
parent
或者像这样将parent
参数curry成createChild
方法中的值(注意makeChild
必须在parent
之后声明):
let createParent parentName childNames =
let rec parent = {
ParentName = parentName
Children = children }
and makeChild = createChild parent
and children = childNames |> List.map makeChild
parent
或者更简单地说:
let createParent parentName childNames =
let rec parent = {
ParentName = parentName
Children = children }
and children = childNames |> List.map (createChild parent)
parent
因为两边都需要是函数而不是属性
试试这个:
let createParent parentName childNames =
let rec children names =
names |> List.map (fun childName -> createChild (parent ()) childName)
and parent () = {
ParentName = parentName
Children = children childNames
}
parent ()