Как вывод типа работает с reduceLeft?

В дополнение к ​​мой другой вопрос о reduceLeft, подпись reduceLeft на Seq

def reduceLeft [B >: A] (f: (B, A) ⇒ B): B 

и мы можем вызывать это с помощью таких выражений, как

List(1,2,3,4) reduceLeft (_ + _)

В этом примере A равно Int, поэтому reduceLeft ожидает Function2[B >: Int, Int, B]. Независимо от того, как работает reduceLeft (что не имеет значения), откуда выводчик типа узнает, что B имеет метод +, если он может быть типа Any?


person Luigi Plinge    schedule 03.12.2011    source источник


Ответы (2)


Я думаю, что раздел 6.26.4 Local Type Inference в spec как бы объясняет, что происходит. Компилятор будет искать оптимальный тип. Когда параметр типа контравариантен, выбранный тип будет максимальным (в данном случае Any), а в противном случае (инвариантным или ковариантным) минимальным (в данном случае Int).

Есть пара примеров, которые я не могу связать с reduceLeft.

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

scala> List(1,2).reduceLeft[Any](_.toString + _)
res26: Any = 12

Но если я не помогу механизму вывода типов:

scala> List(1,2).reduceLeft(_.toString + _)
<console>:8: error: type mismatch;
 found   : java.lang.String
 required: Int
              List(1,2).reduceLeft(_.toString + _)

Edit, я ошибаюсь, учитывается анонимная функция, это работает:

List(1,2).reduceLeft((_:Any).toString + (_:Any).toString)

Есть опция компилятора -Ytyper-debug, с которой вы можете работать:

List(1,2).reduceLeft(_+_)

Он покажет вам, что каким-то образом компилятор предполагает, что ожидаемым типом анонимной функции является (Int, Int) => Int, затем он переходит к проверке _ + _ на соответствие ему и преуспевает, а затем выводит B как Int. Фрагмент здесь:

typed immutable.this.List.apply[Int](1, 2).reduceLeft: [B >: Int](f: (B, Int) => B)B
adapted immutable.this.List.apply[Int](1, 2).reduceLeft: [B >: Int](f: (B, Int) => B)B to ?, undetparams=type B
typing ((x$1, x$2) => x$1.$plus(x$2)): pt = (Int, Int) => Int: undetparams=, 
// some time later 
typed ((x$1: Int, x$2: Int) => x$1.+(x$2)): (Int, Int) => Int
adapted ((x$1: Int, x$2: Int) => x$1.+(x$2)): (Int, Int) => Int to (Int, Int) => Int, 
typed immutable.this.List.apply[Int](1, 2).reduceLeft[Int](((x$1: Int, x$2: Int) => x$1.+(x$2))): Int

Я не знаю, почему при отсутствии описания типа предполагается, что анонимная функция равна (Int, Int) => Int.

person huynhjl    schedule 03.12.2011

Если B>: X и компилятор знает X, но не может разрешить B, он просто принимает B = X.

Это в некоторой степени практично, поскольку у него есть только два варианта для B и известен только один. Таким образом, не зная, какой суперкласс предполагает, что B - это X. Вы можете протестировать процесс принятия решения компилятором с помощью следующего кода.

class Y {
  def bar(y:Y) = this
}
case class X( i: Int ) extends Y {
  def foo(x:X)=X(i+x.i)
}
val t = new Y bar X(7)
val t2 = X(8) bar X(7)
val res = List(X(1),X(2),X(3)) reduceLeft { _ foo _ }
val res2 = List(X(1),X(2),X(3)) reduceLeft { _ bar _ } // will not compile
person Neil Essy    schedule 03.12.2011