【问题标题】:What does Dotty offer to replace type projections?Dotty 提供什么来替换类型投影?
【发布时间】:2018-10-07 04:35:21
【问题描述】:

我一直在阅读有关 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
}

如何在 Dotty 中做这样的事情?向Manager添加第二个类型参数?但是,除了使创建和操作Manager 的实例变得非常乏味之外,它也不太有效,因为没有办法强制这两种类型之间的关系(Manager[FooContainer, Bar] 不应该合法)。

然后,还有其他用途,如类型 lambdas 和部分应用类型,它们对于创建偏向函子等很有用......或者这些(部分应用类型)是否成为 Dotty 中的“一等公民”?

编辑

为了回答 cmets 中的问题,这里有一个他可以使用的有点做作的例子。假设,我的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
}

【问题讨论】:

  • 您能举例说明如何使用 Manager 吗?这样可以更轻松地提出替代设计。
  • @GuillaumeMartres 我在问题中添加了一个示例
  • 您的示例中缺少某些内容,BaseManager 构造函数采用参数storageFooManager 扩展BaseManager 没有参数。
  • @GuillaumeMartres 抱歉,已修复...
  • @Dima Foo-Bar MCVE 中的代码无法编译。 Container 具有泛型,而在 Manager[T &lt;: Container] 中使用时没有泛型。 ContainerTypeManager 中定义了两次。

标签: scala scala-3 dotty type-projection match-types


【解决方案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
}

另一种选择是使用match types

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
}

【讨论】:

  • 在 Dotty 0.17 given [T &lt;: Contents] as ContentType[Container[T]]... 应该代替 implied [T &lt;: Contents] for ContentType[Container[T]]...
  • 在 Dotty 0.20.0-RC1 中 given [T &lt;: Contents]: ContentType[Container[T]] ... 应该代替 given [T &lt;: Contents] as ContentType[Container[T]]...trait Manager[T &lt;: Container[_]] (given val contentType: ContentType[T]) ... 应该代替 trait Manager[T &lt;: Container[_]] given (val contentType: ContentType[T]) ...
猜你喜欢
  • 1970-01-01
  • 2016-05-14
  • 2019-09-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-02-15
  • 2021-12-24
相关资源
最近更新 更多