Почему вывод типа Scala не работает?

У меня этот класс в Scala:

object Util {
  class Tapper[A](tapMe: A) {
    def tap(f: A => Unit): A = {
      f(tapMe)
      tapMe
    }

    def tap(fs: (A => Unit)*): A = {
      fs.foreach(_(tapMe))
      tapMe
    }
  }

  implicit def tapper[A](toTap: A): Tapper[A] = new Tapper(toTap)
}

Теперь,

"aaa".tap(_.trim)

не компилируется, выдавая ошибку

ошибка: отсутствует тип параметра для расширенной функции ((x $1) = > x $1.trim)

Почему тип не выводится как String? Из ошибки кажется, что неявное преобразование действительно срабатывает (в противном случае ошибка будет по линиям "tap не является членом класса String" ). И кажется, что преобразование должно быть в Tapper[String], что означает, что тип аргумента String => Unit (или (String => Unit)*).

Интересно, что если я прокомментирую определение из tap, тогда оно компилируется.

Ответ 1

6.26.3 Разрешение перегрузки

Сначала определяется множество функции, потенциально применимых в зависимости от формы Аргументы

...

Если есть ровно одна альтернатива в B, эта альтернатива выбрана.

В противном случае пусть S1,.,, Sm - вектор типов, полученных путем набора текста каждый аргумент с undefinedожидаемый тип.

Обе перегрузки tap потенциально применимы (на основе "формы" аргументов, в которой учитываются конструкторы arty и type FunctionN).

Таким образом, typer работает так же, как и с:

val x = _.trim

и не работает.

Более разумный алгоритм может принимать наименьшую верхнюю границу соответствующего типа параметра для каждой альтернативы и использовать его как ожидаемый тип. Но эта сложность на самом деле не стоит того, ИМО. В перегрузке много угловых случаев, это лишь другое.

Но есть трюк, который вы можете использовать в этом случае, если вам действительно нужна перегрузка, которая принимает один параметр:

object Util {
  class Tapper[A](tapMe: A) {
    def tap(f: A => Unit): A = {
      f(tapMe)
      tapMe
    }

    def tap(f0: A => Unit, f1: A => Unit, fs: (A => Unit)*): A = {
      (Seq(f0, f1) ++ fs).foreach(_(tapMe))
      tapMe
    }
  }

  implicit def tapper[A](toTap: A): Tapper[A] = new Tapper(toTap)

  "".tap(_.toString)
  "".tap(_.toString, _.toString)
  "".tap(_.toString, _.toString, _.toString)
}