问题描述
我正在尝试执行一个规则,即类型类的(依赖)返回类型本身必须实现一个类型类。因此,当用户实现下面的Isvec
类型类时,他们还必须确保getElem
方法的返回值实现另一个类型类(IsvecElem
)。我试图使这项工作看起来像这样:
// A typeclass for an vector element
abstract class IsvecElem[A,T: Numeric] {
def dataOnly(self: A): T
}
// A typeclass for a vector
abstract class Isvec[A,T: Numeric] {
// I would like this OutElem output type to implement the IsvecElem typeclass
protected type OutElem
def getElem(self: A,i: Int)(implicit tcO: IsvecElem[OutElem,T]): OutElem
}
// give this typeclass method Syntax
implicit class Isvecops[A,T: Numeric](value: A) {
def getElem(i: Int)(implicit tcA: Isvec[A,T],tcO: IsvecElem[tcA.OutElem,T]) = tcA.getElem(value,i)
}
问题与Isvecops中的getElem一起出现-这引发了编译错误:
type OutElem in class Isvec cannot be accessed as a member of Isvec[A,T] from class Isvecops. Access to protected type OutElem not permitted because enclosing class Isvecops is not a subclass of class Isvec
拥有Isvecops
扩展Isvec
并不是一个立即的解决方案,也不是应该的,所以我想知道代码中其他地方的方法是否有错误。
非常感谢任何帮助。
解决方法
IsVecOps
不应扩展IsVec
。隐式类(仅旨在引入扩展方法)扩展类型类将非常奇怪。
如果暂时删除访问修饰符(protected
),您会看到错误消息更改为
illegal dependent method type: parameter may only be referenced in a subsequent parameter section
def getElem...
尝试添加类型参数(OE
)并指定类型细化(IsVec[A,T] { ... }
)
implicit class IsVecOps[A,T: Numeric](value: A) {
def getElem[OE](i: Int)(implicit tcA: IsVec[A,T] { type OutElem = OE },tcO: IsVecElem[OE,T]): OE = tcA.getElem(value,i)
}
如果您介绍Aux
型
object IsVec {
type Aux[A,T,OE] = IsVec[A,T] { type OutElem = OE }
}
然后您可以更紧凑地重写类型细化
implicit class IsVecOps[A,T: Numeric](value: A) {
def getElem[OutElem](i: Int)(implicit tcA: IsVec.Aux[A,OutElem],tcO: IsVecElem[OutElem,T]): OutElem = tcA.getElem(value,i)
}
How can I have a method parameter with type dependent on an implicit parameter?
When are dependent types needed in Shapeless?
Why is the Aux technique required for type-level computations?
Understanding the Aux pattern in Scala Type System
在Dotty中,您将能够使用特征参数,扩展方法,多个隐式参数列表,同一参数列表中彼此依赖的参数类型:
trait IsVecElem[A,T: Numeric] {
def dataOnly(self: A): T
}
trait IsVec[A,T: Numeric] {
protected type OutElem
def (self: A) getElem(i: Int)(using IsVecElem[OutElem,T]): OutElem
}
或
trait IsVecElem[A,T: Numeric] {
def dataOnly(self: A): T
}
trait IsVec[A,T: Numeric] {
/*protected*/ type OutElem
def getElem(self: A,i: Int)(using IsVecElem[OutElem,T]): OutElem
}
extension [A,T: Numeric](value: A) {
def getElem(i: Int)(using tcA: IsVec[A,T],tcO: isVecElem[tcA.OutElem,T]) = tcA.getElem(value,i)
}
(在0.28.0-bin-20200908-ce48f5a-NIGHTLY中测试)