证明嵌套路径相关类型成员的等价性
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 }
}
如果您可以只避免 Container
和 Transform
上的通用参数,您可以通过以下操作让编译器相信这些类型有效:
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 }
}
任何人都可以评论一下这种方法与更高级类型之间的优缺点吗?
这是一个简化的案例,我完全愿意接受 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 }
}
如果您可以只避免 Container
和 Transform
上的通用参数,您可以通过以下操作让编译器相信这些类型有效:
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 }
}
任何人都可以评论一下这种方法与更高级类型之间的优缺点吗?