Во многих учебниках и маркетинговых материалах по Scala я обнаружил, что многие люди злоупотребляют терминологией, смешивая «псевдоним типа» и «зависимый тип», хотя на самом деле это не одно и то же.
Например. в следующем примере TakeAlias
является зависимым типом, а не псевдонимом типа. В результате это приведет к сбою компиляции:
object TrueTypeAlias {
trait Unaliased[+T] {
def take1: List[Seq[(T, T)]]
def take2: List[Seq[(T, T)]]
def take3: List[Seq[(T, T)]]
}
trait Aliased[+T] {
type TakeAlias = List[Seq[(T, T)]]
def take1: TakeAlias
def take2: TakeAlias
def take3: TakeAlias
}
}
/*
TrueTypeAlias.scala:16:10: covariant type T occurs in invariant position in type = List[Seq[(T, T)]] of type TakeAlias
one error found
*/
Проблема в том, что нужно для реализации псевдонима истинного типа? Есть ли механизм/расширение компилятора, которое я могу использовать, чтобы заставить его работать?
Ваш TakeAlias
— это type-alias member
черты Aliased
, которая на самом деле является type-alias
для type
List[Seq[(T, T)]]
. Это то же самое, что иметь в Int value member
имя i
, имеющее значение 10
. И поскольку этот type-alias
является членом trait
... тип, на который ссылается этот type-alias
, также является path dependent type
, поскольку он не может существовать и на него нельзя ссылаться без предварительной ссылки на trait instance
.
Нашел возможное решение:
object TrueTypeAlias {
trait Unaliased[+T] {
def take1: List[Seq[(T, T)]]
def take2: List[Seq[(T, T)]]
def take3: List[Seq[(T, T)]]
}
trait Aliased[+T] {
private type TakeAlias = List[Seq[(T, T)]]
def take1: TakeAlias
def take2: TakeAlias
def take3: TakeAlias
}
}
В качестве альтернативы:
object TrueTypeAlias {
trait Unaliased[+T] {
def take1: List[Seq[(T, T)]]
def take2: List[Seq[(T, T)]]
def take3: List[Seq[(T, T)]]
}
trait Aliased[+T] {
opaque type TakeAlias = List[Seq[(T, T)]]
def take1: TakeAlias
def take2: TakeAlias
def take3: TakeAlias
}
trait Aliased2[+T] extends Aliased[T] {
def take4: TakeAlias
}
}
private
фикс не работает в 2.13 scastie.scala-lang.org/DmytroMitin/iTBIyhmdQNmZLD1QcBJJTgНо private[this]
работает scastie.scala-lang.org/DmytroMitin/iTBIyhmdQNmZLD1QcBJJTg/1 (как сказано в цитате из спецификации в моем ответе). private[this]
отсутствует в Scala 3 docs.scala-lang.org/scala3/reference/dropped-features/…
Возможно, в Scala 3 проверки отклонений были упрощены даже для private
членов. Я не могу найти конкретный PR, документы или проблему. Возможно github.com/lampepfl/dotty/issues/7567 актуально
@DmytroMitin Scala 3 автоматически превращает private
в private[this]
, если элемент используется только как private[this]
. Вы можете увидеть это, если добавите def foo(a: Aliased[?]): a.TakeAlias
.
Всем большое спасибо за новую информацию. Извините, только что понял, что это не относится ни к какой логике, потому что дисперсия - это более доброе понятие. Удалить соответствующую часть
type TakeAlias = List[Seq[(T, T)]]
— это псевдоним типа в том смысле, что TakeAlias
можно использовать вместо List[Seq[(T, T)]]
.
В то же время type TakeAlias = ...
является типовым членом признака. Итак, x.TakeAlias
(для x: Aliased[T]
) — это тип, зависящий от пути. Хотя эта зависимость пути теперь тривиальна: x.TakeAlias
одинаковы для разных x: Aliased[T]
с одним и тем же T
, а именно все x.TakeAlias
являются List[Seq[(T, T)]]
. А так как type TakeAlias = ...
является членом типа, применяются все ограничения позиции отклонения.
Я бы исправил этот код, сделав псевдоним типа универсальным
trait Aliased[+T] {
type TakeAlias[+S] = List[Seq[(S, S)]]
//type TakeAlias[S] = List[Seq[(S, S)]]
def take1: TakeAlias[T]
def take2: TakeAlias[T]
def take3: TakeAlias[T]
}
или извлечение псевдонима типа наружу (например, в компаньон)
trait Aliased[+T] {
import Aliased.*
def take1: TakeAlias[T]
def take2: TakeAlias[T]
def take3: TakeAlias[T]
}
object Aliased {
type TakeAlias[+S] = List[Seq[(S, S)]]
//type TakeAlias[S] = List[Seq[(S, S)]]
}
Цитата из спецификации:
- Правая часть псевдонима типа всегда находится в неизменном положении.
Ссылки на параметры типа в объектно-частных или объектно-защищенных значениях, типах, переменных или методах класса не проверяются на их положение отклонения. В этих элементах параметр типа может появляться где угодно, не ограничивая аннотации допустимых вариантов.
Также, когда вы уверены, что незаконное использование не может привести к проблемам в вашем случае использования, вы можете использовать scala.annotation.unchecked.uncheckedVariance
trait Aliased[+T] {
type TakeAlias = List[Seq[(T @uncheckedVariance, T @uncheckedVariance)]]
def take1: TakeAlias
def take2: TakeAlias
def take3: TakeAlias
}
или
trait Aliased[+T] {
type T1 = T @uncheckedVariance
type TakeAlias = List[Seq[(T1, T1)]]
def take1: TakeAlias
def take2: TakeAlias
def take3: TakeAlias
}
Это и псевдоним типа, и тип, зависящий от исправления, и ни один из них не связан с ошибкой компиляции. - Чтобы просто создать псевдоним типа, вы просто используете ключевое слово
type
в объекте или на верхнем уровне (только Scala 3)