问题
I am trying to enforce a rule that the (dependent) return type of a typeclass, must itself implement a typeclass. So when the user implements the IsVec
typeclass below, they must also ensure that the return value of the getElem
method implements another typeclass (IsVecElem
). My attempts to make this work look something like this:
// 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)
}
The problem comes with getElem in IsVecOps - this raises a compile error:
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
Having IsVecOps
extend IsVec
isn't an immediate solution and doesn't feel like it should be, so I'm wondering if there's an error in approach elsewhere in the code.
Any help much appreciated.
回答1:
IsVecOps
shouldn't extend IsVec
. Implicit class (with the only purpose to introduce an extension method) extending a type class would be very strange.
If for a moment you remove access modifier (protected
) you'll see that the error message changes to
illegal dependent method type: parameter may only be referenced in a subsequent parameter section
def getElem...
Try to add a type parameter (OE
) and specify type refinement (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)
}
If you introduce Aux
-type
object IsVec {
type Aux[A, T, OE] = IsVec[A, T] { type OutElem = OE }
}
then you can rewrite type refinement more compactly
implicit class IsVecOps[A, T: Numeric](value: A) {
def getElem[OutElem](i: Int)(implicit tcA: IsVec.Aux[A, T, 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
In Dotty you'll be able to use trait parameters, extension methods, multiple implicit parameter lists, types of parameters in the same parameter list dependent on each other:
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
}
or
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)
}
(tested in 0.28.0-bin-20200908-ce48f5a-NIGHTLY)
来源:https://stackoverflow.com/questions/63803620/enforcing-that-dependent-return-type-must-implement-typeclass