假设我有一个像这样的愚蠢的小案例类:
case class Foo(name: String, other: Foo)
如何定义a
和b
不可变,a.other
为b
,b.other
为a
? scala是否为"tie the knot"提供了一些方法?我想做这样的事情:
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')
,这很重要。
答案 0 :(得分:13)
您希望Foo
保持不变,但Scala中的懒惰位于声明网站上。 Foo
不可能在不改变它的情况下是非严格的,并且Haskell中指示的模式只能起作用,因为Foo
,那里是非严格的(即Foo "a" b
没有' t立即评估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))