Что Дотти предлагает для замены проекций типа? - PullRequest
0 голосов
/ 26 апреля 2018

Я читал о Дотти, так как похоже, что она собирается стать scala 3, и заметил, что проекции типов считаются "несостоятельными" и удалены из языка ...

Это похоже на облом, поскольку я видел несколько случаев, когда они были действительно полезны. Например:

trait Contents
class Foo extends Contents
class Bar extends Contents

trait Container[T <: Contents] { type ContentType = T }
class FooContainer extends Container[Foo]
class BarContainer extends Container[Bar]

trait Manager[T <: Container[_]] { 
  type ContainerType = T 
  type ContentType = T#ContentType
  def getContents: ContentType 
  def createContainer(contents: ContentType): ContainerType
}

Как можно сделать что-то подобное в Дотти? Добавить параметр второго типа в Manager? Но, помимо того факта, что создание и манипулирование экземплярами Manager делает его действительно утомительным, оно также не вполне работает, поскольку нет способа навязать отношения между двумя типами (Manager[FooContainer, Bar] не должно быть законным).

Затем, есть другие способы использования, такие как лямбда-типы и частично применяемые типы, которые полезны для создания смещенных функторов и т. Д. ... Или эти (частично применяемые типы) становятся "первоклассными гражданами" в Dotty?

РЕДАКТИРОВАТЬ

Чтобы ответить на вопрос в комментариях, вот несколько надуманный пример того, как его можно использовать. Давайте предположим, что мои Managers на самом деле Akka Actors:

abstract class BaseManager[T <: Container[_]](
  val storage: ContentStorage[T#ContentType]
) extends Actor with Manager[T] {
    def withContents(container: T, content: ContentType): ContainerType
    def withoutContents: T

    var container: T = withoutContents

    def receive: Receive {
       case ContentsChanged => 
          container = withContents(container, storage.get)
       case ContainerRequester => 
           sender ! container
       // ... other common actions 
    }
}

class FooManager(storage: FooStorage) extends BaseManager[FooContainer](storage) {
   def withContents(container: FooContainer, content: Foo) = 
       container.copy(Some(content))
   def withoutContent = FooContainer(None)

   override def receive: Receive = super.receive orElse { 
    // some additional actions, specific to Foo
   }
}

case class FooContainer(content: Option[Foo]) extends Container[Foo]{
  // some extremely expensive calculations that happen when 
  // content is assigned, so that we can cache the result in container
}

1 Ответ

0 голосов
/ 30 мая 2019

В Scala 2.12 проекции типов иногда можно заменить на класс типов + типы, зависящие от пути

trait ContentType[T <: Container[_]] {
  type Out
}
object ContentType {
  type Aux[T <: Container[_], Out0] = ContentType[T] { type Out = Out0 }
  def instance[T <: Container[_], Out0]: Aux[T, Out0] = new ContentType[T] { type Out = Out0 }

  implicit def mk[T <: Contents]: Aux[Container[T], T] = instance
}

abstract class Manager[T <: Container[_]](implicit val contentType: ContentType[T]) {
  type ContainerType = T
  def getContents: contentType.Out
  def createContainer(contents: contentType.Out): ContainerType
}

Проверено в Dotty 0.16.0-bin-20190529-3361d44-NIGHTLY (в 0.16.0-RC3 delegate должно быть вместо implied)

trait Contents
class Foo extends Contents
class Bar extends Contents

trait Container[T <: Contents] { type ContentType = T }
class FooContainer extends Container[Foo]
class BarContainer extends Container[Bar]

trait ContentType[T <: Container[_]] {
  type Out
}
object ContentType {
  implied [T <: Contents] for ContentType[Container[T]] {
    type Out = T
  }
}

trait Manager[T <: Container[_]] given (val contentType: ContentType[T]) {
  type ContainerType = T
  type ContentType = contentType.Out
  def getContents: ContentType
  def createContainer(contents: ContentType): ContainerType
}

Еще один вариант - использовать типы соответствия

trait Contents
class Foo extends Contents
class Bar extends Contents

trait Container[T <: Contents] { type ContentType = T }
class FooContainer extends Container[Foo]
class BarContainer extends Container[Bar]

type ContentType[T <: Container[_]] = T match {
  case Container[t] => t
}

trait Manager[T <: Container[_]] {
  type ContainerType = T
  def getContents: ContentType[T]
  def createContainer(contents: ContentType[T]): ContainerType
}
...