Ну, type
не обязательно должно быть конкретным. Вы можете написать:
type Arbitrary
в Ammonite, и он компилируется и запускается. Вы даже можете использовать его в качестве аргумента!
type Arbitrary
def foo(a: Arbitrary): List[Arbitrary] = List(a)
Единственная проблема в том, что компилятор ничего не знает о Arbitrary
(например, что это <: String
или что-то в этом роде), что позволит вам законно создавать значение этого типа.
С этой точки зрения член абстрактного типа - это просто тип, о котором мы ничего не знаем, но который мы можем обойти, зная только то, что он существует, и значение будет иметь этот тип .
Но мы также можем override
это пустое определение, сделав его более конкретным c например,
type Arbitrary = String
type Arbitrary <: AnyVal
type Arbitrary >: User
Тогда тот, кто его реализует, будет иметь доступ к полной информации о типах, а программист написание кода, имеющего абстрактное определение в области видимости, может передавать только тип.
Случайно именно так мы заново открыли типы, зависящие от пути, или, скорее, выяснили, что возможность создавать абстрактные типы отчасти необходима, если мы хотим иметь зависящие от пути типы без поддерживаемого вручную списка случаев, когда они нам не нужны.
В Cats in Scala 2 это имеет другое применение c асе. Существует шаблон, который, по-видимому, был обнаружен Эдвардом Кметтом , который использует член абстрактного типа и .asInstanceOf
для обхода отсутствующих функций polymorphi c, которые мы могли бы поднять до класса типа:
trait DoubleSize[F[_]] {
def double[A](fa: F[A]): F[A]
}
object DoubleSize {
type Arbitrary
def instance[F[_]](fun: F[Arbitrary] => F[Arbitrary]): DoubleSize[F] = new DoubleSize[F] {
def double[A](fa: F[A]): F[A] = fun(fa.asInstanceOf[F[Arbitrary]]).asInstanceOf[F[A]]
}
// in Dotty we could do
// def instance[F[_]](fun: [A] => F[A] => F[A]) = new DoubleSize[F] {
// def double[A](fa: F[A]): F[A] = fun[A](fa)
// }
// but in Scala 2 it's impossible
}
val doubleSize = DoubleSize.instance[List] { list =>
list ++ list
}
doubleSize.double(List(1,2,3))
doubleSize.double(List("a", "b", "c"))
Подобные обходные пути были бы невозможны, если бы мы исключили элементы абстрактного типа, хотя в Дотти этот конкретный обходной путь больше не нужен.