letrec в Scala? (Неизменный способ связать себя узами брака?)

Предположим, у меня есть глупый маленький класс 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 или на верхнем уровне.

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

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

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


person Dan Burton    schedule 06.06.2012    source источник


Ответы (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))
person Daniel C. Sobral    schedule 06.06.2012
comment
Ах, ты прав. Определение data Foo = Foo { name :: !String, other :: !Foo } приводит к тому, что решения Haskell не работают. - person Dan Burton; 06.06.2012
comment
Спасибо за комментарий, иначе Scala всегда будет переоценивать. Это хорошо знать! - person Madoc; 06.06.2012