斯卡拉的letrec?("打结"的不可改变的方式?)

Dan*_*ton 10 scala letrec tying-the-knot

假设我有一个像这样的愚蠢的小案例类:

case class Foo(name: String, other: Foo)
Run Code Online (Sandbox Code Playgroud)

我如何定义ab不可改变这样a.otherb,而且b.othera?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)

绑定到ab包含在同一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" bb立即评估).

否则,解决方案几乎是相同的,允许必要的箍以使所有内容不严格:

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)

  • 啊你说得对。定义 `data Foo = Foo { name :: !String, other :: !Foo }` 会导致 Haskell 解决方案不起作用。 (2认同)