Dan*_*ton 10 scala letrec tying-the-knot
假设我有一个像这样的愚蠢的小案例类:
case class Foo(name: String, other: Foo)
Run Code Online (Sandbox Code Playgroud)
我如何定义a和b不可改变这样a.other的b,而且b.other是a?scala是否提供了"打结"的方法?我想做这样的事情:
val (a, b): (Foo, Foo) = (Foo("a", b), Foo("b", a)) // Doesn't work.
Run Code Online (Sandbox Code Playgroud)
可能性
在Haskell中,我会这样做:
data Foo = Foo { name :: String, other :: Foo }
a = Foo "a" b
b = Foo "b" a
Run Code Online (Sandbox Code Playgroud)
绑定到a和b包含在同一let表达式中或顶层的绑定.
或者,在不滥用Haskell的自动化letrec功能的情况下:
(a, b) = fix (\ ~(a', b') -> Foo "a" b', Foo "b" a')
Run Code Online (Sandbox Code Playgroud)
注意懒惰模式~(a', b'),这很重要.
Dan*_*ral 13
你想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))
Run Code Online (Sandbox Code Playgroud)