Подтвердить что ты не робот

Аннотации вариаций в псевдонимах типов

Недавно я заметил, что аннотации вариаций могут использоваться в псевдонимах типов. Вот пример из 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)

Итак, что мне не хватает? Какова цель аннотации изменений в псевдонимах типов? Можете ли вы привести пример псевдонима типа с аннотациями дисперсии, которые будут отличаться от исходного типа.

4b9b3361

Ответ 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
}