Что предлагает Дотти для замены проекций типа?
Я читал о Дотти, так как похоже, что она собирается стать 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 ContainerType = T#ContentType
def getContents: ContentType
def createContainer(contents: ContentType): ContainerType
}
Как можно сделать что-то подобное в Дотти? Добавьте параметр второго типа в Manager
? Но, помимо того факта, что создание и манипулирование экземплярами Manager
это также не совсем работает, так как нет никакого способа навязать отношения между двумя типами (Manager[FooContainer, Bar]
не должно быть законным).
Кроме того, существуют другие виды использования, такие как лямбда-типы и частично применяемые типы, которые полезны для создания смещенных функторов и т. Д. Или эти (частично применяемые типы) становятся "первоклассными гражданами" в Дотти?
РЕДАКТИРОВАТЬ
Чтобы ответить на вопрос в комментариях, вот несколько надуманный пример, который он может использовать. Давайте предположим, мой Managers
на самом деле Акка 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
}
0 ответов
В Scala 2.12 проекции типов иногда могут быть заменены типом class + путь-зависимые типы
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
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
}