Scala error: "forward reference extends over definition of value" when code appears in a function
Scala error: "forward reference extends over definition of value" when code appears in a function
我正在尝试使用 Scala 2.11.7 编译以下代码。
object LucasSeq {
val fibo: Stream[Int] = 0 #:: 1 #:: fibo.zip(fibo.tail).map { pair =>
pair._1 + pair._2
}
def firstKind(p: Int, q: Int): Stream[Int] = {
val lucas: Stream[Int] = 0 #:: 1 #:: lucas.zip(lucas.tail).map { pair =>
p * pair._2 - q * pair._1
}
lucas
}
}
fibo
基于 Fibonacci sequence example in Scala's Stream
documentation,并且有效。
然而,试图用参数 p
和 q
概括序列的 firstKind
函数(使得 Lucas sequences of the first kind)出现以下错误:
LucasSeq.scala:7: error: forward reference extends over definition of value lucas
val lucas: Stream[Int] = 0 #:: 1 #:: lucas.zip(lucas.tail).map { pair =>
^
one error found
基本是一样的代码,为什么在函数外有效,在函数内却不行?
这个错误信息让我之前的很多程序员都疑惑过。我考虑过……
- So just don't put that code in a function — 但我 想要一个函数。
implicit val lucas
— 没有帮助。
- Self-references can only be used in lazy expressions — 但是这个 是 懒惰,对吧?
- Compile with
-Xprint:typer
diagnostics — 不确定如何处理该信息。
- Is it a shadowing issue? — 不,我使用的标识符不会冲突。
- Compiler bug? — 我希望不会。引用的错误应该已经在 2.11.7 中修复了。
我可能会继续阅读几个小时,但我认为此时最好寻求帮助。我正在寻找解决方案和解释。 (我熟悉函数式编程,但对 Scala 不熟悉,所以如果解释涉及 "synthetic" 和 "implicit" 等术语,那么我可能还需要对此进行额外解释。)
这里有一个回答,但不知为何被删除了。
基本上有两种选择。您可以将 val
变成 lazy val
。或者您可以将 class 中的 lucas: Stream[Int]
定义为字段。您可以在构造函数中使用 p
和 q
参数化 class。
你是对的,原来的代码是偷懒的。但是scala翻译它还不够懒。
为了简单起见,考虑一下 val a = 1 + a
将翻译什么代码(我知道代码没有多大意义)。在 Java int a = 1 + a
中将不起作用。 Java 将尝试在 1 + a
中使用 a
,但 a
尚未初始化。即使 Java 有 Integer a = 1 + a
,并且 a
将是一个引用,Java 仍然无法执行此操作,因为 Java 运行 1 + a
语句分配 a
时
所以它留给我们两个选择。定义 a
不是变量,而是字段。 Scala 通过定义递归方法而不是字段来自动解决问题 - 因为 Scala 中的字段无论如何都是两种方法 + 变量。或者您可以明确地告诉 scala 它应该通过将您的 val 指定为 lazy val
来解决此处的惰性问题。这将使 scala 生成一个隐藏的 class,其中包含所有必要的基础设施,使其变得懒惰。
您可以通过 运行 带有 -print
选项的编译器来检查此行为。虽然输出相当复杂,尤其是在 lazy val
的情况下。
另请注意,因为您的流离开了范围,也因为您的流有两个参数 - p
和 q
,如果您使用 lazy val
选项。如果您选择创建一个额外的 class - 您可以通过为每个 p
和 q
可能的
缓存此 class 的所有实例来控制它
P.S。我在这里说 Java
当然是指 JVM。用 Java
来思考更容易
我正在尝试使用 Scala 2.11.7 编译以下代码。
object LucasSeq {
val fibo: Stream[Int] = 0 #:: 1 #:: fibo.zip(fibo.tail).map { pair =>
pair._1 + pair._2
}
def firstKind(p: Int, q: Int): Stream[Int] = {
val lucas: Stream[Int] = 0 #:: 1 #:: lucas.zip(lucas.tail).map { pair =>
p * pair._2 - q * pair._1
}
lucas
}
}
fibo
基于 Fibonacci sequence example in Scala's Stream
documentation,并且有效。
然而,试图用参数 p
和 q
概括序列的 firstKind
函数(使得 Lucas sequences of the first kind)出现以下错误:
LucasSeq.scala:7: error: forward reference extends over definition of value lucas
val lucas: Stream[Int] = 0 #:: 1 #:: lucas.zip(lucas.tail).map { pair =>
^
one error found
基本是一样的代码,为什么在函数外有效,在函数内却不行?
这个错误信息让我之前的很多程序员都疑惑过。我考虑过……
- So just don't put that code in a function — 但我 想要一个函数。
implicit val lucas
— 没有帮助。- Self-references can only be used in lazy expressions — 但是这个 是 懒惰,对吧?
- Compile with
-Xprint:typer
diagnostics — 不确定如何处理该信息。 - Is it a shadowing issue? — 不,我使用的标识符不会冲突。
- Compiler bug? — 我希望不会。引用的错误应该已经在 2.11.7 中修复了。
我可能会继续阅读几个小时,但我认为此时最好寻求帮助。我正在寻找解决方案和解释。 (我熟悉函数式编程,但对 Scala 不熟悉,所以如果解释涉及 "synthetic" 和 "implicit" 等术语,那么我可能还需要对此进行额外解释。)
这里有一个回答,但不知为何被删除了。
基本上有两种选择。您可以将 val
变成 lazy val
。或者您可以将 class 中的 lucas: Stream[Int]
定义为字段。您可以在构造函数中使用 p
和 q
参数化 class。
你是对的,原来的代码是偷懒的。但是scala翻译它还不够懒。
为了简单起见,考虑一下 val a = 1 + a
将翻译什么代码(我知道代码没有多大意义)。在 Java int a = 1 + a
中将不起作用。 Java 将尝试在 1 + a
中使用 a
,但 a
尚未初始化。即使 Java 有 Integer a = 1 + a
,并且 a
将是一个引用,Java 仍然无法执行此操作,因为 Java 运行 1 + a
语句分配 a
所以它留给我们两个选择。定义 a
不是变量,而是字段。 Scala 通过定义递归方法而不是字段来自动解决问题 - 因为 Scala 中的字段无论如何都是两种方法 + 变量。或者您可以明确地告诉 scala 它应该通过将您的 val 指定为 lazy val
来解决此处的惰性问题。这将使 scala 生成一个隐藏的 class,其中包含所有必要的基础设施,使其变得懒惰。
您可以通过 运行 带有 -print
选项的编译器来检查此行为。虽然输出相当复杂,尤其是在 lazy val
的情况下。
另请注意,因为您的流离开了范围,也因为您的流有两个参数 - p
和 q
,如果您使用 lazy val
选项。如果您选择创建一个额外的 class - 您可以通过为每个 p
和 q
可能的
P.S。我在这里说 Java
当然是指 JVM。用 Java