Letrec в Scala? (Неизменяемый способ "связать узел?" )

Предположим, что у меня есть такой же глупый класс case case:

case class Foo(name: String, other: Foo)

Как я могу определить a и b неизменно, чтобы a.other был b, а b.other - a? Предоставляет ли scala способ "связать узел" ? Я хотел бы сделать что-то вроде этого:

val (a, b): (Foo, Foo) = (Foo("a", b), Foo("b", a)) // Doesn't work.

Возможности

В Haskell я бы сделал это:

data Foo = Foo { name :: String, other :: Foo }

a = Foo "a" b
b = Foo "b" a

Если привязки к a и b содержатся в одном и том же выражении let или на верхнем уровне.

Или, не злоупотребляя возможностями Haskell Automagical letrec:

(a, b) = fix (\ ~(a', b') -> Foo "a" b', Foo "b" a')

Обратите внимание на ленивый шаблон ~(a', b'), что важно.

Ответ 1

Вы хотите, чтобы Foo остался без изменений, но лень в Scala находится на сайте объявления. Нельзя Foo быть нестрогим, не меняя его, и шаблон, указанный в Haskell, работает только потому, что Foo, там, является нестрогим (т.е. Foo "a" b не оценивает b немедленно).

В противном случае решение почти то же самое, позволяя обручам, чтобы все было нестрогим:

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))