证明嵌套路径相关类型成员的等价性

Proving equivalence of nested path dependent types members

这是一个简化的案例,我完全愿意接受 different/better 实现此目的的方法

trait Container {
  type T
  def data: List[T]
}

trait Transform {
  val from: Container
  val to: Container
  def xform: from.T => to.T
}

case class Identity(c: Container) extends Transform {
  val from = c
  val to = c
  def xform = { t: from.T => t }
}

这会产生可预测的误差:

<console>:12: error: type mismatch;
 found   : t.type (with underlying type Identity.this.from.T)
 required: Identity.this.to.T
         def xform = { t: from.T => t }

目标基本上是有一个转换容器底层对象的转换,但能够说服类型检查器(没有到处都是可怕的可怕转换)类型是相同的。

能够以这种方式显示类型的等价关系和关系的最佳方式是什么?

就像我说的,完全愿意重构代码,我保证在实际示例中它是为了真正的目的:)

因为 Container 中的类型没有公开,所以您无法推断出 Identity class 的任何信息。想一想如果您从另一个库中使用它,如果您只是给定 Identity 对象,则无法知道 xform 是什么类型。实际上,您可以使用 xform 的唯一定义是:

def xform = { t: from.T => to.data.head }

您唯一可以调用的方法是 from.data.head

另一种方法是删除路径相关类型并使用更高种类的类型:

  trait Container[T] {
    def data: List[T]
  }

  trait Transform[A, B] {
    val from: Container[A]
    val to: Container[B]
    def xform: A => B
  }

  case class Identity[A](c: Container[A]) extends Transform[A, A] {
    val from = c
    val to = c
    def xform = { t: A => t }
  }

我认为通用参数可能是描述此模式的更简单方法。

但是,您可以通过在两个 Container 实例上显式标识类型 T 来避免使用泛型参数:

case class Identity(c: Container) extends Transform {
  val from: Container { type T = c.T } = c
  val to: Container { type T = c.T } = c
  def xform = { t: from.T => t }
}

或者更简单:

case class Identity(c: Container) extends Transform {
  val from: c.type = c
  val to: c.type = c
  def xform = { t: from.T => t }
}

如果您可以只避免 ContainerTransform 上的通用参数,您可以通过以下操作让编译器相信这些类型有效:

case class Identity[U](c: Container { type T = U }) extends Transform {
  val from = c
  val to = c
  def xform = { t: c.T => t }
}

通用参数 U 除了给 Container 参数上的 T 类型赋予另一个名称外,什么都不做,但这足以达到目的!

对我来说,所有这些解决方案实际上只是强调了当涉及到这些类型时,类型检查器的威力是多么的随意。我真的无法解释为什么它们是必要的或充分的。根据我的经验,使用泛型参数更容易预测(当然它可能会更混乱)。

如果一个人曾经被依赖类型困住,结构优化是一种丑陋但有效的纾困措施。

abstract class ContainerRefiner {
  val container: Container
}

trait Container {
  type T
  def data: List[T]
}

trait Transform {
  val from: Container
  val to: Container
  def xform: from.T => to.T
}

case class Identity(c: Container) extends Transform {
  val refiner = new {val container: c.type = c} with ContainerRefiner
  val from = refiner.container
  val to = refiner.container
  def xform = { t: from.T => t }
}

任何人都可以评论一下这种方法与更高级类型之间的优缺点吗?