Как механизм вывода типов работает на reduLeft?
В дополнение к моему другому вопросу о 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]
, Независимо от того, как работает reduLeft (что не имеет значения), как механизм определения типов знает, что B
имеет +
метод, когда это может быть типа Any
?
2 ответа
Я думаю, что раздел 6.26.4 Локальный вывод типа спецификации вроде объясняет, что происходит. Компилятор будет искать оптимальный тип. Когда параметр типа является контравариантным, выбранный тип будет максимальным (в данном случае 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 + _)
Редактировать, я ошибаюсь, анонимная функция учитывается, это работает:
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
,
Если 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