首页 文章

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

提问于
浏览
3

Cay Horstmann的书“Scala for the Impatient”中有关于apply方法的注释:

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

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

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

或明确致电申请:

"Bonjour".sorted.apply(3)

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

("Bonjour".sorted)(3)

sorted方法返回 String ,可以将其明确转换为 StringOps ,括号用于包装字符串表达式 . 为什么编译器不接受调用 StringOps 的apply方法?

1 回答

  • 2

    您可以使用 -Xprint:parser 查看早期丢弃的parens:

    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>
    

    额外的parens什么都不做 . 编译器只看到一个应用程序 expr(args) . 因为它's an application, you don'得到"implicit application"转换 .

    在任何情况下,方法 scaled 的含义取决于预期的类型 .

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

    可能规范实际上是明确的:

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

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

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

    当写为 e.apply(arg) 时, e 不是像 e(arg) 这样的应用程序,因此您可以从隐式应用程序等转换中受益 .

相关问题