2016-11-07 2 views
4

Я попытался скомпилировать следующий фрагмент кода:ковариации в Scala

class A[-T] 
class B[-T] extends A[A[T]] 

я получаю следующее сообщение об ошибке:

error: contravariant type T occurs in covariant position in type [-T]A[A[T]]{def <init>(): B[T]} of class B 

Почему это ошибка?

+0

'' - это конструктор, и он определяется неявно. – rightfold

+0

Тогда почему я могу определить 'class B [-T]'? Также будет существовать метод 'init' и вернуть мне« B [T] », и он компилируется отлично. –

ответ

2

<init> является конструктором A (или B), определяемым компилятором.

Проблема с кодом заключается в том, что B[-T] extends A[A[T]] приводит к противоречию.

Возьмем такой пример:

class A[-T] 
class B[-T] extends A[A[T]] 

class Animal 
class Cat extends Animal 

Для коротких рук, давайте использовать A <: B, чтобы показать, что A является подтипом B.

Так как A и B являются контравариантными над их параметрами типа T и Cat <: Animal, а затем

A[Animal] <: A[Cat] 
B[Animal] <: B[Cat] 

С A[Animal] <: A[Cat], а затем (опять же из-за контрвариации из A):

A[A[Cat]] <: A[A[Animal]] 

Кроме , по определению B:

B[Cat] = A[A[Cat]] 
B[Animal] = A[A[Animal]] 

Мы уже знаем, что B[Animal] <: B[Cat], но если перевести эти типы в их выше equivalances, мы получаем:

B[Animal] <: B[Cat] => A[A[Animal]] <: A[A[Cat]] 

Но мы уже показали, что A[A[Cat]] <: A[A[Animal]]. В итоге у нас сложилась невозможная ситуация.

+0

Неверно, что B [Cat] = A [A [Cat]]. В лучшем случае у вас есть B [Cat] <: A [A [Cat]] –

+0

Это, правда. «B [Cat]» - это «A [A [Cat]]», так как «List [A]» также является «Seq [A]» или «Int» является «AnyVal». Если класс что-то расширяет, то это _is_ эта вещь. Верхняя граница не является строгой. –

+0

Тот факт, что 'B [Cat]' является 'A [A [Cat]]', не является равенством, потому что мы говорим о типах. Ваше окончательное значение подразумевает неверное предположение, что если 'A <: B',' C <: D' и 'A <: C', тогда мы будем иметь' B <: D', что, очевидно, неверно. Нет, мне очень жаль, но ваш ответ неправильный. В большинстве случаев вы получаете следующее: 'B [Animal] <: B [Cat] <: A [A [Cat]] <: A [A [Animal]' –

4

Это ошибка, потому что это A[A[T]]ковариантной в типе T.

Определение типа ковариантной от Wikipedia:

Within the type system of a programming language, a typing rule or a type constructor is:

  • covariant if it preserves the ordering of types (≤), which orders types from more specific to more generic;
  • contravariant if it reverses this ordering;

Рассмотрим следующий пример:

class Fruit 
class Banana extends Fruit 

и

Banana <: Fruit 
A[Banana] :> A[Fruit] // Because A is contravariant in type T 
A[A[Banana]] <: A[A[Fruit]] // Again because A is contravariant in type T 

Последнее заявление означает, что A[A[T]] является covariant, поскольку он сохраняет порядок типов от более конкретных до более общих.

Так что можно сделать:

scala> type AA[+T] = A[A[T]] 
defined type alias AA 

scala> type AAA[-T] = A[A[A[T]]] 
defined type alias AAA 

Но следующее приведет к ошибке:

scala> type AA[-T] = A[A[T]] 
<console>:15: error: contravariant type T occurs in covariant position in type [-T]A[A[T]] of type AA 
     type AA[-T] = A[A[T]] 
      ^

scala> type AAA[+T] = A[A[A[T]]] 
<console>:15: error: covariant type T occurs in contravariant position in type [+T]A[A[A[T]]] of type AAA 
     type AAA[+T] = A[A[A[T]]] 
      ^

И, наконец, возвращаясь к первоначальному вопросу есть одно и то же нарушение правила дисперсии в class B поскольку базовый класс A[A[T]] является ковариантным по типу T по строительству.

Чтобы понять, почему это запрещено давайте предположим, что это возможно:

class B[-T] extends A[A[T]] 

В этом случае мы получаем:

B[Fruit] <: B[Banana] <: A[A[Banana]] 

так

val fruits: B[Fruit] = new B[Fruit] 
val bananas1: B[Banana] = fruits 
val bananas2: A[A[Banana]] = bananas1  

Теперь мы имеем значение bananas2 типа A[A[Banana]], который указывает на экземпляр A[A[Fruit]], который нарушает безопасность типа с A[A[Banana]] <: A[A[Fruit]].

+0

Итак, вы правы, я не могу определить тип AA [-T] = A [A [T]] '. Но для моего первоначального вопроса, как насчет определения класса, который * расширяет * 'A [A [T]]', но определяет 'T' как контравариантный параметр? –

+0

@ MikaëlMayer Я обновил ответ, чтобы покрыть исходный вопрос. Взгляни, пожалуйста. – dkolmakov