这是一个简化的案例,我完全愿意采用不同/更好的方式来实现这个目标
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 }
目标基本上是有一个变换来转换容器底层的对象,但是能够说服类型检查器(没有可怕的可怕的演员阵容),类型是相同的。
能够以这种方式显示类型的等价和关系的最佳方法是什么?
就像我说的那样,完全开放重构代码,我在实际例子中承诺它是出于真正的目的:)
答案 0 :(得分:4)
我认为通用参数可能是描述此模式的更简单方法。
但是,您可以通过在T
个实例上明确标识Container
类型来避免通用参数:
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
除了为T
参数上的Container
类型指定其他名称外什么也不做,但这足以说明问题了!
对我来说,所有这些解决方案实际上只是强调了类型检查器在这些类型中的功能看似随意。我无法解释为什么它们是必要的或充分的。根据我的经验,使用通用参数更加可预测(尽管当然它可能更加混乱)。
答案 1 :(得分:0)
由于Container
中的类型未公开,因此无法推断Identity
类的任何内容。想想你是否从另一个库中使用它,如果刚刚给出了xform
对象,就无法知道Identity
的类型。实际上,您可以用于xform
的唯一定义是:
def xform = { t: from.T => to.data.head }
您可以拨打的唯一方法是使用from.data.head
。
另一种方法是删除路径依赖类型并使用更高级的kinded类型:
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 }
}
答案 2 :(得分:0)
如果一个人坚持依赖类型,结构改进是一个丑陋但有效的纾困。
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 }
}
任何人都可以评论这种方法与更高级别的类型之间的利弊吗?