Чем Дотти предлагает заменить проекции типов?

Я читал о Dotty, так как похоже, что он скоро станет 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
}

person Dima    schedule 26.04.2018    source источник
comment
Можете ли вы привести пример того, как вы бы использовали Manager? Так было бы легче придумать альтернативный дизайн.   -  person Guillaume Martres    schedule 27.04.2018
comment
@GuillaumeMartres Я добавил пример к вопросу   -  person Dima    schedule 29.04.2018
comment
В вашем примере чего-то не хватает, конструктор BaseManager принимает параметр storage, но FooManager расширяет BaseManager без аргументов.   -  person Guillaume Martres    schedule 30.04.2018
comment
@GuillaumeMartres извините, исправил ...   -  person Dima    schedule 30.04.2018
comment
@Dima Код в Foo-Bar MCVE не компилируется. Container имеет общий, а в Manager[T <: Container] он используется без универсального. ContainerType определяется дважды в Manager.   -  person Dmytro Mitin    schedule 30.05.2019
comment
@DmytroMitin, ладно, исправил. Для протокола, я хотел, чтобы это была просто иллюстрация подхода к дизайну, а не рабочий пример для реальной реализации, поэтому не уделял особого внимания правильному синтаксису.   -  person Dima    schedule 30.05.2019
comment
@Dima Спасибо, но теперь он не компилируется: Error: covariant type ContentType occurs in contravariant position in type Manager.this.ContentType of value content. Ну, я просто не хочу догадываться, что ты имеешь в виду.   -  person Dmytro Mitin    schedule 30.05.2019
comment
Я не уверен ... У меня он компилируется нормально в репл. Кроме того, я не уверен, что его компиляция обязательно поможет вам понять, что я имею в виду. Если намерение неясно, возможно, просто спросите?   -  person Dima    schedule 30.05.2019
comment
@Dima scastie.scala-lang.org/qcVdzhLoTke5RHTL6oMtJg   -  person Dmytro Mitin    schedule 30.05.2019
comment
@Dima Может быть, def createContainer[U >: ContentType](contents: U): ContainerType должно быть вместо def createContainer(contents: ContentType): ContainerType. В любом случае, подойдет ли вам мой ответ?   -  person Dmytro Mitin    schedule 04.06.2019


Ответы (1)


В 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
}
person Dmytro Mitin    schedule 30.05.2019
comment
В Dotty 0.17 вместо implied [T <: Contents] for ContentType[Container[T]]... должно быть given [T <: Contents] as ContentType[Container[T]]... - person Dmytro Mitin; 07.09.2019
comment
В Dotty 0.20.0-RC1 given [T <: Contents]: ContentType[Container[T]] ... должно быть вместо given [T <: Contents] as ContentType[Container[T]]... и trait Manager[T <: Container[_]] (given val contentType: ContentType[T]) ... должно быть вместо trait Manager[T <: Container[_]] given (val contentType: ContentType[T]) ... - person Dmytro Mitin; 20.11.2019
comment
users.scala-lang. org / t / - person Dmytro Mitin; 21.08.2020