Аннотации вариаций в псевдонимах типов
Недавно я заметил, что аннотации вариаций могут использоваться в псевдонимах типов. Вот пример из Predef
:
type Function[-A, +B] = Function1[A, B]
И я начал думать, где его можно было использовать. Очевидно, вы не можете изменить дисперсию на противоположную, или сделать инвариантный тип, чтобы вести себя как ко- или контравариантный. Компилятор выдает ошибку, например,
scala> type BrokenFunc[+T, -R] = Function1[T, R]
<console>:7: error: covariant type T occurs in contravariant position in type
[+T, -R]T => R of type BrokenFunc
Но вы можете сделать некоторый тип варианта, чтобы вести себя как инвариант (по крайней мере, компилятор не стал бы спорить с этим). Итак, я попытался сделать инвариантную версию List
scala> type InvList[T] = List[T]
defined type alias InvList
Но этот новый инвариант List
все еще ведет себя так же, как и исходная ковариантная версия:
scala> val l: InvList[String] = List("foo")
l: InvList[String] = List(foo)
scala> val anyList: InvList[Any] = l
anyList: InvList[Any] = List(foo)
Итак, что мне не хватает? Какова цель аннотации изменений в псевдонимах типов? Можете ли вы привести пример псевдонима типа с аннотациями дисперсии, которые будут отличаться от исходного типа.
Ответы
Ответ 1
Итак, я не знаю точно, но я собираюсь дать возможное объяснение.
Типичные псевдонимы в Scala довольно "слабы"; они не полностью создают новые типы, просто новые способы написания старых типов (и новые типы, зависящие от пути); это означает, что если вы определяете
type InvList[T] = List[T]
и напишите InvList[T]
, это точно так, как если бы вы написали List[T]
; поэтому InvList[Int] <: InvList[Any]
, потому что, переписан, это просто List[Int] <: List[Any]
. На самом деле я точно не знаю, как "слабый" Scala тип псевдонимов... они немного сильнее Haskell из-за зависимых от пути типов, но слабее, чем объявления классов. Возможно, кто-то еще может объяснить дальше.
Итак, почему Scala позволяет размещать там аннотации вариаций, если они просто игнорируют их и вообще переписывают тип? Это для членов типа. Это так, что вы можете сказать
trait A { type F[+T] }
и требуют, чтобы реализации соответствовали дисперсии +T
, поэтому вы разрешаете
trait B extends A { type F[+T] = List[T] }
но не
trait C extends A { type F[T] = Function[T,T] }
Или, как из этого предложения из Scala Language Spec S4.3.
Объявление конструктора типа накладывает дополнительные ограничения на конкретные типы для которых t может стоять. Помимо границ L и U, предложение параметра типа могут налагать ограничения и отклонения более высокого порядка, определяемые соответствием конструкторов типов (п. 3.5.2).
Ответ 2
Если абстрактный тип объявлен только (как в Domain
), инвариантность будет применена. Если тип абстрактности имеет более расслабленную дисперсию, это считается некогда известным (как в DomainImpl
):
trait Domain {
type InvList[T]
val xi: InvList[Int]
private val xa: InvList[Any] = xi // won't compile
}
class DomainImpl extends Domain {
type InvList[T] = List[T]
val xi: InvList[Int] = List(1)
private val xa: InvList[Any] = xi // will compile
}