7

Consider the following simplistic example:

type Parent = { Children : Child list }
and Child = { Value : int ; Parent : Parent }

let rec children = [ { Value = 0 ; Parent = parent } ]
and parent = { Children = children }

The F# compiler is smart enough to correctly initialize these recursive objects, as can be verified by running

obj.ReferenceEquals(parent, parent.Children.Head.Parent)

Now, consider the following generalization:

let length = 100 // assume arbitrary

let rec children = List.init length (fun i -> { Value = i ; Parent = parent })
and parent = { Children = children }

This definition will result in a compiler error. My question is the following: is there any way I could make the above binding without resorting to reflection or mutable fields?

eirik
  • 608
  • 3
  • 11
  • 1
    As a F# newbie, I would be interested in knowing what is the practical application of a code snippet like the one above. – Knows Not Much Feb 12 '14 at 20:27
  • 2
    The example itself is just a meaningless simplification that I knocked together. The more general problem is that of initializing cyclic instances of immutable data structures. – eirik Feb 12 '14 at 20:43

2 Answers2

15
let rec mkChild i = {Value = i; Parent = parent}
and parent = { Children = children }
and children = List.init length mkChild
desco
  • 16,642
  • 1
  • 45
  • 56
  • 2
    So, what precisely are the limitations? I couldn't find anything in the spec. – Daniel Feb 12 '14 at 20:17
  • That's slick - I tried binding children with list comprehension and that fails in this form and the original form too. – plinth Feb 12 '14 at 20:23
0

I can't answer for F#, but I'm sure there is no way to do that in OCaml which is very close to F#. This is the same as defining a circular list:

let rec a = 1 :: 2 :: a;

In the case of recursive object definition, then you can't have function call along the recursive cycle because it means calling a function on a object which is not yet fully constructed.

More precisely in your example, you are trying to pass to List.init a closure (fun i -> { Value = i ; Parent = parent }) but this closure can't be defined since parent is not yet fully defined.

hivert
  • 10,579
  • 3
  • 31
  • 56