Scala 当typeclass中有typeclass时,使用typeclass语法的正确方法是什么?
我有一种情况,我使用一个typeclass-in-a-typeclass来重载原始typeclass的方法。示例如下:Scala 当typeclass中有typeclass时,使用typeclass语法的正确方法是什么?,scala,inner-classes,typeclass,implicit,Scala,Inner Classes,Typeclass,Implicit,我有一种情况,我使用一个typeclass-in-a-typeclass来重载原始typeclass的方法。示例如下: abstract class IsArray[A, T: Numeric] { def getSingleElem(self: A, idx: Int): T def getRef[R](self: A, ref: R)(implicit refTc: RefTC[R]): refTc.Out = refTc.getRef(self, ref) trait Ref
abstract class IsArray[A, T: Numeric] {
def getSingleElem(self: A, idx: Int): T
def getRef[R](self: A, ref: R)(implicit refTc: RefTC[R]): refTc.Out = refTc.getRef(self, ref)
trait RefTC[R] {
type Out
def getRef(self: A, ref: R): Out
}
object RefTC {
implicit val numsTcForSingleInt = new RefTC[Int] {
type Out = T
def getRef(self: A, ref: Int): Out = getSingleElem(self, ref)
}
implicit val numsTcForListInt = new RefTC[List[Int]] {
type Out = List[T]
def getRef(self: A, ref: List[Int]): Out = ref.map(getSingleElem(self, _))
}
}
}
这一切都很好。我遇到的困难是为typeclass创建一个“syntax”对象,因此可以直接从实现typeclass的值调用这些方法。我的第一次尝试如下所示,类型检查正常:
object IsArraySyntax {
implicit class IsArrayOps1[A, T: Numeric](self: A)(implicit isArTc: IsArray[A, T]) {
def getSingleElem(idx: Int): T = isArTc.getSingleElem(self, idx)
def getRef[R](ref: R)(implicit refTc: isArTc.RefTC[R]): refTc.Out = refTc.getRef(self, ref)
}
}
然而,我在使用这个(例如)的时候遇到了一些奇怪的错误,我想知道我写这个的方式是否是罪魁祸首。IsArrayOps1
类与refTc-typeclass之间存在有效的依赖关系,如果isArTc
typeclass是方法的参数,而不是IsArrayOps1
类的参数,则这将成为显式依赖关系,如:
object IsArraySyntax {
implicit class IsArrayOps2[A, T: Numeric](self: A) {
def getSingleElem(idx: Int)(implicit isArTc: IsArray[A, T]): T = isArTc.getSingleElem(self, idx)
def getRef[R](ref: R)(implicit isArTc: IsArray[A, T], refTc: isArTc.RefTC[R]): refTc.Out = refTc.getRef(self, ref)
}
}
这不是类型检查,可能需要使Aux
模式工作
但我更想知道这里的最佳实践是什么?将
isArTc
typeclass作为隐式类的属性,而不是每个方法,似乎可以减少样板文件并简化typeclass依赖关系,但我没有看到它被使用过,我想知道它是否出于其他原因而不受欢迎?refTc:isArTc.refTc[R]
是属于另一个typeclass的typeclass的正确语法,还是应该更像refTc:IsArray #refTc[R]
?嵌套类型类使用得更少,但原则上可以使用它们
嵌套类型类的另一个示例是
这不是类型检查,可能需要使Aux
模式工作
否,Aux
模式将不起作用Aux
模式有助于处理类型参数/类型成员中的依赖项,但不能处理前缀中的依赖项,如IsArrayOps2
。这种依赖性在Scala 2中是无法表达的
实际上,将隐式参数拆分为类级参数和方法级参数(如IsArrayOps1
)是解决这种依赖性的正确方法
它是否应该更像refTc:IsArray#refTc[R]
不,类型投影不能很好地使用隐式分辨率
您可以检查类型投影的语法是否不起作用
我在使用这个时遇到了一些奇怪的错误(例如使用ScalaTest时的java.lang.NoSuchFieldError),我想知道这是否是我写这个的方式造成的
您的类型类和语法#1似乎工作正常
case class MyClass(is: List[Int])
object MyClass {
implicit val mcIsIntArray: IsArray[MyClass, Int] = new IsArray[MyClass, Int] {
override def getSingleElem(self: MyClass, idx: Int): Int = self.is(idx)
}
implicit val mcIsDoubleArray: IsArray[MyClass, Double] = new IsArray[MyClass, Double] {
override def getSingleElem(self: MyClass, idx: Int): Double = self.is(idx)
}
}
val ia = implicitly[IsArray[MyClass, Int]]
implicitly[ia.RefTC[Int] { type Out = Int}]
implicitly[ia.RefTC[List[Int]] { type Out = List[Int]}]
val ia1 = implicitly[IsArray[MyClass, Double]]
implicitly[ia1.RefTC[Int] { type Out = Double}]
implicitly[ia1.RefTC[List[Int]] { type Out = List[Double]}]
implicitly[IsArray[MyClass, Int]].getSingleElem(MyClass(List(1, 2, 3)), 1) // 2
implicitly[IsArray[MyClass, Int]].getRef(MyClass(List(1, 2, 3)), 1) // 2
implicitly[IsArray[MyClass, Int]].getRef(MyClass(List(1, 2, 3)), List(1, 0)) // List(2, 1)
implicitly[IsArray[MyClass, Double]].getSingleElem(MyClass(List(1, 2, 3)), 1) // 2.0
implicitly[IsArray[MyClass, Double]].getRef(MyClass(List(1, 2, 3)), 1) // 2.0
implicitly[IsArray[MyClass, Double]].getRef(MyClass(List(1, 2, 3)), List(1, 0)) // List(2.0, 1.0)
import IsArraySyntax._
{
import Numeric.IntIsIntegral // to avoid ambiguity
MyClass(List(1, 2, 3)).getSingleElem(1): Int
MyClass(List(1, 2, 3)).getRef(1): Int
MyClass(List(1, 2, 3)).getRef(List(1, 0)): List[Int]
}
{
import Numeric.DoubleIsFractional // to avoid ambiguity
MyClass(List(1, 2, 3)).getSingleElem(1): Double
MyClass(List(1, 2, 3)).getRef(1): Double
MyClass(List(1, 2, 3)).getRef(List(1, 0)): List[Double]
}
我将implicitsNumeric.IntIsIntegral
,Numeric.doubleisfrancial
导入到相应的作用域,以避免像中那样的歧义
顺便说一下,您可以用一个类型类来表达相同的逻辑,该类添加了更多的类型参数和条件隐式(listIsArray
)
或者只是
abstract class IsArray[A, T: Numeric, Col[_], Out] {
def getRef(self: A, ref: Col[T]): Out
}
在我们的例子中,Col
可以是Id
或List
object IsArraySyntax {
implicit class IsArrayOps4[A, T: Numeric, R, Out](self: A)(implicit
isAr: IsArray[A, T, R, Out]
) {
def getSingleElem(idx: Int)(implicit ev: Int <:< R): Out =
isAr.getRef(self, idx)
def getRef(ref: R): Out = isAr.getRef(self, ref)
}
}
abstract class IsArray[A, T: Numeric, Col[_], R <: Col[T], Out] {
def getRef(self: A, ref: R): Out
}
abstract class IsArray[A, T: Numeric, Col[_], Out] {
def getRef(self: A, ref: Col[T]): Out
}