带有后缀表示法的toList on Range会导致类型不匹配

时间:2016-06-20 21:26:07

标签: scala

我刚开始使用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

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 Documentation

中指定的这种风格令人不悦
  

后缀表示法

     

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)并将其复制为代码块,您将看到相同的行为。

可在此Scala user-group question

中找到更多信息

答案 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"));
//...