letrec in Scala? (Immutable way to "Tie the knot?")

letrec, scala, tying-the-knot

Solution

You want `Foo` to be unchanged, but laziness in Scala is on the declaration site. It is impossible for `Foo` to be non-strict without changing it, and the pattern indicated in Haskell only works because `Foo`, there, is non-strict (that is, `Foo "a" b` doesn't evaluate `b` immediately).

Otherwise the solution is pretty much the same, allowing for the hoops necessary to get everything non-strict:

class Foo(name: String, other0: => Foo) { // Cannot be case class, because that mandates strictness
  lazy val other = other0 // otherwise Scala will always reevaluate
}
object Foo {
  def apply(name: String, other: => Foo) = new Foo(name, other)
}

val (a: Foo, b: Foo) = (Foo("a", b), Foo("b", a))

Problem

Suppose I have a stupid little case class like so: ``` case class Foo(name: String, other: Foo) ``` How can I define `a` and `b` immutably such that `a.other` is `b`, and `b.other` is `a`? Does scala provide some way to "tie the knot"? I'd like to do something like this: ``` val (a, b): (Foo, Foo) = (Foo("a", b), Foo("b", a)) // Doesn't work. ``` Possibilities In Haskell I would do this: ``` data Foo = Foo { name :: String, other :: Foo } a = Foo "a" b b = Foo "b" a ``` Where the bindings to `a` and `b` are contained in the same `let` expression, or at the top level. Or, without abusing Haskell's automagical letrec capabilities: ``` (a, b) = fix (\ ~(a', b') -> Foo "a" b', Foo "b" a') ``` Note the lazy pattern, `~(a', b')`, that's important.

Original source

Related problems