Летрек в Скала? (Неизменный способ "завязать узел?")
Предположим, у меня есть глупый маленький класс 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 возможностями 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))