Scala 应用方法调用作为括号与隐式参数冲突



Cay Horstmann的《Scala for the Impatient》一书中有一个关于应用方法的注释:

有时,((表示法会与另一个Scala功能冲突: 隐式参数。例如,表达式"Bonjour".sorted(3)产生错误,因为可以选择调用排序的方法 使用排序,但 3 不是有效的排序。

解决方案是将"Bonjour".sorted赋值给变量并对其调用 apply,例如:

val result = "Bonjour".sorted
result(3)

或显式调用应用:

"Bonjour".sorted.apply(3)

但是为什么这不起作用并产生编译错误:

("Bonjour".sorted)(3)

排序方法返回一个String,可以非正式地转换为StringOps,括号用于包装字符串表达式。 为什么编译器不接受调用StringOps的应用方法?

您可以使用-Xprint:parser来查看括号是否提前被丢弃:

scala> implicit class x(val s: String) { def scaled(implicit i: Int) = s * i }
defined class x
scala> "hi".scaled(5)
res0: String = hihihihihi
scala> { implicit val n: Int = 5 ; "hi".scaled }
res1: String = hihihihihi
scala> "hi".scaled(5)(3)
res2: Char = i
scala> { implicit val n: Int = 5 ; ("hi".scaled)(3) }
res3: String = hihihi
scala> :se -Xprint:parser
scala> { implicit val n: Int = 5 ; ("hi".scaled)(3) }
[[syntax trees at end of                    parser]] // <console>
package $line8 {
object $read extends scala.AnyRef {
def <init>() = {
super.<init>();
()
};
object $iw extends scala.AnyRef {
def <init>() = {
super.<init>();
()
};
import $line3.$read.$iw.$iw.x;
object $iw extends scala.AnyRef {
def <init>() = {
super.<init>();
()
};
val res4 = {
implicit val n: Int = 5;
"hi".scaled(3)
}
}
}
}
}
res4: String = hihihi
scala> 

额外的括号什么都不做。编译器只看到一个应用程序expr(args)。因为它是一个应用程序,所以不会获得"隐式应用程序"转换。

无论如何,scaled的含义 ,一种方法,取决于预期的类型。

我们期望额外的括号有所作为的原因是括号会覆盖运算符的优先级。但(x)只是x.

可能规范实际上很清楚这一点:

e(args)要求e适用于args。特别是,根据e的参数类型对参数进行类型检查。

如果e是一个值,则e(args)被视为e.apply(args),但scaled是一个方法。

您希望使用"隐式应用程序"来插入隐式参数,但这仅适用于尚未应用e的情况。或者(e)(args)可以被视为(e(_))(args),即(x => e(x))(arg)

当编写为e.apply(arg)时,e不像e(arg)那样是一个应用程序,所以你可以从隐式应用程序这样的转换中受益。

最新更新