2010-07-23 3 views
11

меня this class в 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) 

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

error: missing parameter type for expanded function ((x$1) => x$1.trim)

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

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

ответ

17

6.26.3 Overloading Resolution

One first determines the set of functions that is potentially applicable based on the shape of the arguments

...

If there is precisely one alternative in B, that alternative is chosen.

Otherwise, let S1, . . . , Sm be the vector of types obtained by typing each argument with an undefined expected type.

Оба перегруженные tap потенциально применимы (на основе «формы» аргументов, на долю которых приходится арностью и конструкторами типа 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) 
} 
+0

Отличная идея, спасибо! Я думал, что мне придется назвать их по-другому. –

+3

Ты быстро становишься новым Даниэлем, Джейсоном! –

+2

@oxbow Даже лучше, он часто цитирует спецификацию, что хорошо. –

Смежные вопросы