Почему Scala полностью не вводит параметры типа при вложенности параметров типа?
Рассмотрим следующий код Scala:
abstract class A
abstract class B[T <: A]
class ConcreteA extends A
class ConcreteB extends B[ConcreteA]
class Example[U <: B[T], T <: A]( resolver: U )
object Test {
new Example( new ConcreteB )
}
Последняя строка new Example( new ConcreteB )
не скомпилируется со следующей ошибкой:
error: аргументы inferred type [ConcreteB, Nothing] не соответствуют классу. Ограничения параметра типа типа [U <: B [T], T <: A]
Но ConcreteB
имеет все необходимые данные для разрешения как U, так и T. Что мне здесь не хватает?
Ответы
Ответ 1
Киптон вплотную приблизился к своему высокопоставленному решению. К сожалению, он споткнулся о том, что кажется ошибкой в Scala < 2.9.1.RC1. Следующее работает как ожидается с 2.9.1.RC1 и туловищем,
Welcome to Scala version 2.9.1.RC1 (Java HotSpot(TM) Server VM, Java 1.7.0).
Type in expressions to have them evaluated.
Type :help for more information.
scala> abstract class A
defined class A
scala> abstract class B[T <: A]
defined class B
scala> class ConcreteA extends A
defined class ConcreteA
scala> class ConcreteB[T <: A] extends B[T]
defined class ConcreteB
scala> class Example[T <: A, U[X <: A] <: B[X]](resolver: U[T])
defined class Example
scala> new Example(new ConcreteB[ConcreteA])
res0: Example[ConcreteA,ConcreteB] = [email protected]
Ответ 2
(См. также два связанных вопроса: Scala не выводит правильные аргументы типа и Тип, посланный к Nothing в Scala)
Похоже на ограничение вывода типа Scala, которое намеренно не указано. В процессе работы вы можете получить вывод, сделав T
членом типа B
, а не параметром,
abstract class A
abstract class B { type T <: A }
class ConcreteA extends A
class ConcreteB extends B { type T = ConcreteA }
class Example[U <: B]( resolver: U )
object Test {
new Example( new ConcreteB )
}
При использовании членов типа полезно знать, что они могут отображаться как параметры типа, используя уточнение, как в Miles Sabin: Почему эта циклическая ссылка с типом нелегальной?
В ответ Jean-Philippe Pellet на связанный вопрос, типу вывода помогли сделать параметр типа более высоким. Если вы вводите дополнительный параметр типа в ConcreteB
, тогда может быть введен тип вывода,
abstract class A
abstract class B[T <: A]
class ConcreteA extends A
class ConcreteB[T <: A] extends B[T]
class Example[T <: A, U[T0 <: A] <: B[T0]]( resolver: U[T] )
object Test {
new Example( new ConcreteB[ConcreteA] )
}
Scala 2.9 дает загадочное сообщение об ошибке ниже, но Майлз Сабин указывает, что это ошибка, которая будет исправлена для 2.9.1
<console>:15: error: kinds of the type arguments (ConcreteA,ConcreteB[T0]) do not conform to the expected kinds of the type parameters (type T,type U) in class Example.
ConcreteB[T0] type parameters do not match type U expected parameters: class ConcreteB has one type parameter, but type U has one
new Example( new ConcreteB[ConcreteA] )
^
Ответ 3
Я написал документ обходных методов вывода типа в GitHub для моего собственного обучения.
Несколько простых правил, которые я считаю полезными, следующие:
-
Параметры типа параметров типа не могут быть выведены: Scala вывод типа видит только типы, указанные в списке параметров (не путать с списком параметров типа).
-
Предыдущие параметры не используются для определения будущих параметров: Информация типа распространяется только через списки параметров, а не параметры.
Однако в этом конкретном примере члены типа - это путь вперед (спасибо @Kipton Barros!)