Если следующее компилируется, не нуждаясь в явном определении типа на this
?
def prepList[B >: A](prefix: PlayList[B]) : PlayList[B] =
prefix.foldr(this: PlayList[B])((node, suffix) => suffix.prepNode(node))
Мне кажется, что тип должен смочь к выведенному. Это - просто ограничение в компиляторе Scala или является там теоретической типом причиной, что это не может быть сделано? У меня действительно нет чувства еще для того, что тип Scala inferencer, как могут ожидать, обработает.
Работа через тот метод:
B >: A
по определениюthis
имеет тип PlayList[A]
, который является подтипом PlayList[B]
с тех пор B >: A
и PlayList является ковариантным в A
.node
имеет тип B
, тип параметра prefix
.f
в foldr
имеет тот же тип (объявленный B
) как первый параметр к foldr
.suffix
имеет тот же тип как this
, так в особенности это - a PlayList[A]
. С тех пор B >: A
, suffix.prepNode()
берет a B
.Я хотел бы, чтобы компилятор видел это suffix.prepNode(node)
законно где node
имеет тип B
. Это, кажется, может сделать это, только если я явно указываю тип на вызове foldr
или на ссылке на this
в том вызове.
Интересно, если я указываю явные типы на параметрах функции как (node: B, suffix: PlayList[B])
, ошибка несоответствия типов все еще сгенерирована на параметре к вызову метода suffix.prepNode(node)
: "found: B, required: A"
Я использую Scala 2.8 RC6. Полный пример ниже, рассматриваемая строка является строкой 8.
sealed abstract class PlayList[+A] {
import PlayList._
def foldr[B](b: B)(f: (A, B) => B): B
def prepNode[B >: A](b: B): PlayList[B] = nel(b, this)
def prepList[B >: A](prefix: PlayList[B]): PlayList[B] =
// need to specify type here explicitly
prefix.foldr(this: PlayList[B])((node, suffix) => suffix.prepNode(node))
override def toString = foldr("")((node, string) => node + "::" + string)
}
object PlayList {
def nil[A]: PlayList[A] = Nil
def nel[A](head: A, tail: PlayList[A]): PlayList[A] = Nel(head, tail)
def nel[A](as: A*): PlayList[A] = as.foldRight(nil[A])((a, l) => l.prepNode(a))
}
case object Nil extends PlayList[Nothing] {
def foldr[B](b: B)(f: (Nothing, B) => B) = b
}
case class Nel[+A](head: A, tail: PlayList[A]) extends PlayList[A] {
def foldr[B](b: B)(f: (A, B) => B) = f(head, tail.foldr(b)(f))
}
Править: вторая попытка рассуждать через шаги компиляции
foldr
берет параметры типов (T)((U, T) => T)
. Мы пытаемся вывести значения типов U
и T
.foldr
и второй параметр к функции - они - то же самое, T
. (В частичном ответе Daniel.)this: PlayList[A]
и suffix: PlayList[B]
B >: A
, самый определенный общий супер тип PlayList[B]
; поэтому мы имеем T == PlayList[B]
. Обратите внимание, что нам не нужны никакие отношения между U
и T
вывести это.Это - то, где я застреваю:
node
имеет тип B
(то есть, U == B
).U == B
не выводя его из параметра типа suffix
. (scala компилятор может сделать это?)U == B
, и мы скомпилировали успешно. Таким образом, какой шаг пошел не так, как надо?РЕДАКТИРОВАНИЕ 2: В переименовании foldr
параметр вводит выше, я пропустил это U == A
по определению это - параметр типа PlayList
класс. Я думаю, что это все еще согласовывается с вышеупомянутыми шагами, хотя, так как мы называем его на экземпляре PlayList[B]
.
Таким образом на сайте вызова, T == PlayList[B]
как наименее общий супертип нескольких вещей, и U == B
по определению foldr
на получателе. Это кажется достаточно кратким для сужения к нескольким опциям:
B
PlayList[B]
из foldr
к типу параметра prepNode
(скептически настроенный)Я не эксперт по типам, но вот что происходит, когда я пытаюсь сделать вывод.
((узел, суффикс) => суффикс.prepNode (узел))
возвращает некоторый неизвестный тип PlayList [T]
, где T расширяет A. Он передается в качестве аргумента функции foldr, которая возвращает тип переданной ему функции ( PlayList [T]
, где T расширяет A). И это должен быть какой-то тип PlayList [B]
.
Итак, я предполагаю, что this: PlayList [B]
необходим, чтобы указать, что T и B связаны.
Может быть, вам нужно, чтобы PlayList был параметрическим в двух типах PlayList [+ A, B>: A]
, поскольку у вас есть PrepNode и propList, которые, похоже, работают с тем же типом, который расширяет A?
Иными словами, ваше исходное определение класса могло быть определено следующим образом:
def prepNode[T >: A](b: T): PlayList[T]
def prepList[U >: A](prefix: PlayList[U]): PlayList[U]
Но вы использовали B в обоих случаях, и компилятор не знает, что T и U одинаковы.
Edit, вы можете поиграть с опцией -explaintypes и посмотреть, что делает компилятор в зависимости от подсказок типа, которые вы получаете. Вот результат выполнения команд объяснения и удаления : PlayList [B]
(с 2.8.0.RC1):
$ scalac -explaintypes -d classes Infer.scala
found : node.type (with underlying type B)
required: A
prefix.foldr(this)((node, suffix) => suffix.prepNode(node))
^
node.type <: A?
node.type <: Nothing?
B <: Nothing?
<notype> <: Nothing?
false
Any <: Nothing?
<notype> <: Nothing?
false
false
false
false
B <: A?
B <: Nothing?
<notype> <: Nothing?
false
Any <: Nothing?
<notype> <: Nothing?
false
false
false
Any <: A?
Any <: Nothing?
<notype> <: Nothing?
false
false
false
false
false
Надеюсь, это поможет пролить свет.Может возникнуть вопрос о том, когда scalac может делать выводы, а когда не может делать выводы, было бы полезно.
Проблема в том, что foldr
не указывает B>: A
, поэтому, насколько foldr
что касается его собственных типов A
и B
, нет никакой связи. Что касается foldr
, суффикс
и узел
совершенно не связаны - даже если вы случайно передали ему связанные параметры.