【问题标题】:Scala apply method call as parentheses conflicts with implicit parametersScala应用方法调用作为括号与隐式参数冲突
【发布时间】:2018-05-09 18:08:30
【问题描述】:

Cay Horstmann 的书《不耐烦的 Scala》中有一条关于 apply 方法的注释:

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

解决方法是将"Bonjour".sorted赋值给一个变量,然后调用apply,例如:

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

或者显式调用apply:

"Bonjour".sorted.apply(3)

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

("Bonjour".sorted)(3)

sorted 方法返回一个String,可以隐式转换为StringOps,括号用于包裹字符串表达式。 为什么编译器不接受调用StringOps 的apply 方法?

【问题讨论】:

    标签: scala apply implicit-parameters


    【解决方案1】:

    您可以使用-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的参数类型对args进行类型检查。

    如果e 是一个值,而scaled 是一个方法,则将e(args) 视为e.apply(args)

    您希望“隐式应用程序”插入隐式参数,但这仅适用于尚未应用 e 的情况。或者说(e)(args)可以看成(e(_))(args),也就是(x =&gt; e(x))(arg)

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

    【讨论】:

    • 因此,("Bonjour".sorted) 周围的括号不会强制 Scala 编译器调用排序函数,而是作为函数引用进行计算,但 sorted(_) 之后的括号会执行函数调用。
    • 我写了不同的词,但花了我更长的时间。额外的括号什么都不做。编译器只看到一个应用程序expr(args)。因为它是一个应用程序,所以您不会获得“隐式应用程序”转换。嘿,我做到了。我也会添加这些词。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-02-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-07-25
    • 2016-12-23
    相关资源
    最近更新 更多