Бег с -Xprint:typer
горит здесь. Проблема с test2
что существует экзистенциальный тип, который появляется в двух отдельных местах: оба
write.value
и write.writer
имеют экзистенциальный тип, но, главное,
компилятор не может знать, что они имеют то же самое экзистенциально
квантифицированная переменная типа. Даже если вы получаете доступ к ним с одного и того же объекта,
компилятор забывает, что они пришли из одного места.
Когда test1
напечатан полностью, вы увидите:
def test1(set: Set[Write[_]], cache: Cache) =
set.foreach(((x0$1: Write[_]) => x0$1 match {
case (value: _$1, writer: Writer[_$1])Write[_$1]((value @ _), (writer @ _)) =>
cache.store[_$1](value, writer)
}));
Переменная типа _$1
сопоставляется со значениями. Соответствие переменной типа _$1
связывает ее в
область действия case
, так что она больше не является экзистенциальной, и Scala может сказать
что value
и writer
имеют один и тот же параметр типа.
Решение для test2
состоит в том, чтобы не использовать экзистенции:
def test2[A]( set: Set[ Write[ A ]], cache: Cache ) {
set.foreach { write =>
cache.store( write.value, write.writer )
}
}
или связать переменную типа с совпадением:
def test2( set: Set[ Write[ _ ]], cache: Cache ) {
set.foreach { case write: Write[a] =>
cache.store( write.value, write.writer )
}
}
редактировать
Позвольте мне постараться ответить на новые вопросы, которые вы подняли.
Причина test3
не работает, в том, что когда вы пишете:
set.foreach (процесс)
process
, который является полиморфным, должен быть сделан мономорфным по двум причинам (которые я знаю):
Функции в Scala не могут быть полиморфными; только методы могут быть. process
как определено как метод; при использовании в качестве первоклассной функции это функция.
Способ, которым компилятор делает вывод типов, в основном заключается в получении полиморфных значений и их объединении, чтобы сделать их менее полиморфными. Передача фактического полиморфного значения в качестве аргумента метода потребует типов более высокого ранга.
Причина, по которой test4
работает , состоит в том, что функция литерал:
set.foreach( w => process( w ))
на самом деле не является полиморфной функцией! В качестве аргумента он принимает экстенсивно определенный тип; но не полиморфный тип. Затем он вызывает метод (не функция) process
и сопоставляет переменную экзистенциального типа с параметром типа process
. Довольно дикий, а?
Вы также можете написать:
set.foreach( process(_) )
, что, создавая анонимную функцию, означает то же самое.
Другим маршрутом, который вы можете или не можете найти подходящий, будет отказ от
экзистенциальные типы и члены использования типов:
trait Writable {
type A
val value: A
val writer: Writer[A]
}
case class Write[T]( value: T, writer: Writer[ T ]) extends Writable {
type A = T
}
def test2( set: Set[Writable], cache: Cache ) {
set.foreach { write =>
cache.store( write.value, write.writer )
}
}
Здесь Scala может видеть, что write.value
и write.writer
имеют одинаковые
Параметр типа, потому что они имеют одинаковый тип, зависящий от пути.