4

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?

Shaido
  • 27,497
  • 23
  • 70
  • 73
Guram Savinov
  • 594
  • 5
  • 10

1 Answers1

3

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.

som-snytt
  • 39,429
  • 2
  • 47
  • 129
  • So, parentheses around ("Bonjour".sorted) doesn't force Scala compiler to call sorted function, but evaluates as a function reference, but parentheses after sorted(_) does a function call. – Guram Savinov May 09 '18 at 20:03
  • I wrote different words, but it took me longer to do. 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. Hey, I did it. I'll add those words, too. – som-snytt May 09 '18 at 21:16