2014-10-18 3 views
4

Следующий код не компилируется (в Scala 2.11):Как псевдоним ковариантный универсальный параметр типа

case class CovariantClass[+R](value: R) { 
    type T = R 
    def get: R = value 
} 

object Main { 
    def main(args: Array[String]): Unit ={ 
    println(CovariantClass[String]("hello").get) 
    } 
} 

Сообщение об ошибке:

Error:(4, 8) covariant type R occurs in invariant position in type R of type T 
    type T = R 
    ^

Почему я не могу псевдоним параметр ковариантного типа? Если я удалю строку type T = R, код компилирует и печатает hello, поэтому проблема с псевдонимом. К сожалению, это означает, что я не могу создать псевдоним для более сложных типов, например, type T = List[R] тоже не скомпилирован, хотя List является ковариантным.

ответ

4

От scala spec:

Правая сторона типа псевдонима всегда в инвариантной позиции.

Это означает, что вы не можете создать псевдоним T и указать вариант типа R на правой стороне. То же самое относится к List[R], так как это также ковариантно.

Вы можете, однако предоставить псевдоним типа с параметром типа:

case class CovariantClass[+R](value: R) { 
    type T[+R] = List[R] 
    def get: R = value 
} 

Если вы обнаружили, желая псевдоним параметра типа R, вы, вероятно, следует просто назвать это что-то другое, в первую очередь ,

+0

Благодарим вас за ответ и за цитирование спецификации scala. Я согласен с переименованием 'R' вместо его наложения, но я думал, что имеет псевдоним для чего-то вроде« Либо [ReturnValueInCaseOfFailure, R] ». К сожалению, работа, которую вы предлагаете, делает параметризацию «T», но, вероятно, она ближе всего к тому, что я хотел. –

3

Это запрещено, потому что это позволит программе неправильно, это всегда правило. Вы можете переписать это следующим образом:

case class CovariantClass[+R](value: R) { 
    type T <: R 
    def get: R = value 
} 

Что касается примера того, как она ломается, подумайте:

case class CovariantClass[+R](value: R) { 
    type T = Int 
    def get: R = value 
    def put(x: T) {} 
    def put2(x: R) {} 
} 

Из-за того, как T определено, оно инвариантно. Это означает, что он может использоваться в местах, где ковариантные типы не могут, например, видели выше. Обратите внимание, что put компилируется, но put2 нет.

+0

Интересно. Хотя, в этом примере, 'R' должен быть нижней границей для' T', для того, чтобы код сломался, правильно? Кроме того, я не вижу, как я мог бы сломать его, не введя привязку к типу. Это просто консервативный дизайн? В любом случае: Спасибо за пример. –

+0

@KuluLimpa Я добавил пример проблем, которые могут возникнуть. Это только начало примера, чтобы показать, как это связано с проблемами дисперсии. Если вам нужна дополнительная информация, просмотрите вопросы о дисперсии - должны быть примеры того, почему варианты типов в определенных позициях являются незаконными (я знаю, что я писал некоторые раньше :)). –