简体   繁体   中英

Scala apply method call as parentheses conflicts with implicit parameters

There is a note in Cay Horstmann's book "Scala for the Impatient" about the apply method:

Occasionally, the () notation conflicts with another Scala feature: implicit parameters. For example, the expression "Bonjour".sorted(3) yields an error because the sorted method can optionally be called with an ordering, but 3 is not a valid ordering.

The solution is to assign "Bonjour".sorted to a variable and call apply on it, for example:

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

Or call apply explicitly:

"Bonjour".sorted.apply(3)

But why this doesn't work and produces a compile error:

("Bonjour".sorted)(3)

The sorted method returns a String , which can be imlicitly converted to a StringOps and parentheses are used to wrap the string expression. Why compiler doesn't accept to call the apply method of a StringOps ?

You can use -Xprint:parser to see that the parens are discarded early:

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> 

The extra parens do nothing. The compiler just sees an application expr(args) . Because it's an application, you don't get "implicit application" conversion.

In any case, the meaning of scaled , a method, depends on the expected type.

The reason we expect the extra parens to make a difference is that parens override precedence of operators. But (x) is just x .

Possibly the spec is actually clear about this:

e(args) requires that e be applicable to the args . In particular, the args are typechecked according to the parameter types of e .

e(args) is taken as e.apply(args) if e is a value, but scaled is a method.

You're hoping for "implicit application" to insert the implicit args, but that only applies when e is not already applied. Or that (e)(args) could be taken as (e(_))(args) , that is, (x => e(x))(arg) .

When written as e.apply(arg) , the e is not an application like e(arg) , so you benefit from conversions like implicit application.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM