2

The following code results in a compiler error, "FS0695: This recursive binding uses an invalid mixture of recursive forms."

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

What does the error message mean? How should I correct this code?

The code is intended to initialize F# immutable records with circular references. I tried to apply the solutions here and here, but without success.

Community
  • 1
  • 1
Wallace Kelly
  • 15,565
  • 8
  • 50
  • 71

2 Answers2

4

I'm not familiar with this error and it doesn't appear to be documented anywhere, but here's what I can surmise after playing around with it a bit.

The issue seems to be in how the parent is closed over by the lambda expression, which itself is inside the definition of children. This apparently throws off the type checker (tc.fs)—my guess is because the right side of the children binding itself contains an expression (the lambda expression) that itself contains a recursive reference, but the true cause may be more subtle than this. Notice that if you reverse the declarations like this:

let createParent parentName childNames =
    let rec parent = {
        ParentName = parentName
        Children = children }
    and children = childNames |> List.map (fun childName -> createChild parent childName)
    parent

You'll also see this warning on the line where children is being bound:

Warning: This and other recursive references to the object(s) being defined will be checked for initialization-soundness at runtime through the use of a delayed reference. This is because you are defining one or more recursive objects, rather than recursive functions.

To fix this, it seems you can pull the closure out one level:

let createParent parentName childNames =
    let rec makeChild n = createChild parent n 
    and parent = {
        ParentName = parentName
        Children = children }
    and children = childNames |> List.map makeChild
    parent

Or curry the parent parameter into the value into createChild method like this (notice that makeChild must be declared after parent):

let createParent parentName childNames =
    let rec parent = {
        ParentName = parentName
        Children = children }
    and makeChild = createChild parent
    and children = childNames |> List.map makeChild
    parent

Or more simply like this:

let createParent parentName childNames =
    let rec parent = {
        ParentName = parentName
        Children = children }
    and children = childNames |> List.map (createChild parent)
    parent
p.s.w.g
  • 146,324
  • 30
  • 291
  • 331
  • Can you confirm that we must have a separate `rec` function like `makeChild`. It seems that a curried function will work too. `List.map (createChild parent)` instead of `List.map makeChild` seems to work, too, and be simpler. – Wallace Kelly Jun 17 '14 at 15:56
  • @Wally Yep you're right, it seems to be an issue with how the `parent` is closed over by the lambda expression. I wasn't able to find any documentation on the error, but I was able to determine that it's thrown by the type checker ([tc.fs](https://github.com/fsharp/FSharp.Compiler.Service/blob/master/src/fsharp/tc.fs#L3492)). – p.s.w.g Jun 17 '14 at 16:17
  • @Wally I've updated my answer for completeness. I've provided more detail on my research and some other alternatives. – p.s.w.g Jun 17 '14 at 16:51
0

Because both sides need to be functions rather than properties

Try this instead:

let createParent parentName childNames =
    let rec children names =
        names |> List.map (fun childName -> createChild (parent ()) childName)
    and parent () = {
        ParentName = parentName
        Children = children childNames
    }
    parent ()
Simon Dickson
  • 711
  • 7
  • 10