我刚开始使用Scala,并在Range和List上尝试了一些东西,我用一个非常简单的片段得到了一些非常奇怪的东西。我使用sublime来编辑和执行这些片段:
val a = 1 to 10
println(a)
产量
Range(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
而
val a = 1 to 10
val b = a toList
println(a)
给我错误:
/home/olivier/Dropbox/Projects/ProjectEuler/misc/scala/ch05_ex02.scala:5: error: type mismatch;
found : Unit
required: Int
println(a)
^
one error found
相反,在REPL中,我没有收到任何错误。 Scala版本是2.9.2
答案 0 :(得分:4)
这是由编译器解析Suffix Notation的方式引起的(对于arity 0的方法)。它会尝试将其解析为 Infix Notation (如果可能)。这会导致编译器解析您的代码,如下所示:
val a = 1 to 10
val b = a toList println(a)
或者特别是带有点符号的后一行:
val b = a.toList.apply(println(a))
List[A]
有一个apply
方法,使用A
类型的 varargs (在本例中为Int
)和println
返回Unit
。这就是这个特定错误消息的原因。
后缀表示法
Scala允许使用后缀表示法调用arity-0的方法:
names.toList
// is the same as
names toList // Unsafe, don't use!
此样式不安全,不应使用。由于分号是可选的,编译器会尝试将其视为中缀方法(如果它可以),可能会从下一行中取一个术语。
names toList
val answer = 42 // will not compile!
这可能会导致意外的编译错误,并且最糟糕的是编译错误的代码。虽然某些DSL使用了这种语法,但应该将其视为已弃用,并避免使用。
从Scala 2.10开始,使用后缀运算符表示法将导致编译器警告。
根据建议,使用点符号:
val b = a.toList
或者,如果你真的想要,添加一个分号来表示行尾:
val b = a toList;
注意后者将发出编译器警告,如文档中所述:
[warn] postfix operator toList should be enabled
[warn] by making the implicit value scala.language.postfixOps visible.
[warn] This can be achieved by adding the import clause 'import scala.language.postfixOps'
[warn] or by setting the compiler option -language:postfixOps.
[warn] See the Scaladoc for value scala.language.postfixOps for a discussion
[warn] why the feature should be explicitly enabled.
[warn] val b = a toList;
[warn] ^
[warn] one warning found
相反,在REPL中,我没有收到任何错误。
因为REPL逐行执行。由于toList
表达式未被println
表达式继承,因此它将进行编译。如果您输入粘贴模式(:paste
)并将其复制为代码块,您将看到相同的行为。
答案 1 :(得分:0)
使用-Xprint:parser,typer
查看解析代码的方式以及推断出的类型。答案解释了后缀和中缀解析的相互作用;但错误让你离开,“但toList
甚至没有Int
。”
$ scala -Xprint:parser,typer
scala> :pa
// Entering paste mode (ctrl-D to finish)
1 to 10 toList
println("hi")
// Exiting paste mode, now interpreting.
[[syntax trees at end of parser]] // <console>
package $line3 {
object $read extends scala.AnyRef {
// ...
val res0 = 1.to(10).toList(println("hi"))
// ...
[[syntax trees at end of typer]] // <console>
package $line3 {
object $read extends scala.AnyRef {
//...
private[this] val <res0: error>: <error> = scala.this.Predef.intWrapper(1).to(10).toList.apply(println("hi"));
//...