Член абстрактного типа одноэлементного объекта - PullRequest
1 голос
/ 18 июня 2020

Метод абстрактного члена недопустим в одноэлементном объекте

scala> object Foo {
     |   def g: Int
     | }
         def g: Int
             ^
On line 2: error: only traits and abstract classes can have declared but undefined members

как абстрактный член-значение

scala> object Foo {
     |   val x: Int
     | }
         val x: Int
             ^
On line 2: error: only traits and abstract classes can have declared but undefined members

, однако член абстрактного типа разрешен в одноэлементном object

scala> object Foo {
     |   type A
     | }
object Foo

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

Ответы [ 2 ]

5 голосов
/ 18 июня 2020

Ну, 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"))

Подобные обходные пути были бы невозможны, если бы мы исключили элементы абстрактного типа, хотя в Дотти этот конкретный обходной путь больше не нужен.

3 голосов
/ 18 июня 2020

На основе @ tpolecat и Dmytro кажется, что член абстрактного типа связан с экзистенциальным типом

scala> object O {
     |   type A
     |   implicitly[A <:< (x forSome { type x })]
     | }
object O

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...