Chr*_*per 2 scala implicit inner-classes typeclass
我有一种情况,我使用 typeclass-within-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 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, _))
}
}
}
Run Code Online (Sandbox Code Playgroud)
这一切正常。我遇到困难的地方是为类型类创建“语法”对象,因此可以直接从实现类型类的值调用这些方法。我的第一次尝试看起来像这样并且类型检查正常:
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)
}
}
Run Code Online (Sandbox Code Playgroud)
但是,我在使用它时遇到了一些奇怪的错误(例如,使用 ScalaTest 时的 java.lang.NoSuchFieldError),我想知道是否应该怪我写这个的方式。isArTctypeclass 和 refTc typeclass之间实际上存在依赖关系,如果 typeclassisArTc是方法的参数而不是类的参数,则它成为显式依赖关系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)
}
}
Run Code Online (Sandbox Code Playgroud)
这不会进行类型检查,并且可能需要使Aux模式起作用。
但我更想知道这里的最佳实践是什么?将isArTctypeclass 作为 theimplicit class而不是 each 方法的属性似乎可以减少样板文件并简化 typeclass 依赖性,但我没有看到它被使用过,我想知道它是否由于其他原因不受欢迎?refTc: isArTc.RefTC[R]属于另一个类型类的类型类的正确语法是正确的,还是应该更像refTc: IsArray#RefTC[R]?
嵌套类型类使用较少,但原则上可以使用。
嵌套类型类的另一个例子是如何避免在 Scala 中调用 asInstanceOf
这不会进行类型检查,并且可能需要使
Aux模式起作用。
不,Aux模式无济于事。Aux模式有助于解决类型参数/类型成员中的依赖关系,但不能帮助解决像 in 这样的前缀中的依赖关系IsArrayOps2。这种依赖在 Scala 2 中无法表达。
实际上,将隐式参数拆分为类级别和方法级别的参数(如 in IsArrayOps1)是应对这种依赖的正确方法。
它应该更像
refTc: IsArray#RefTC[R]吗?
不,类型投影在隐式分辨率下效果不佳
https://typelevel.org/blog/2015/07/23/type-projection.html
您可以使用类型投影检查您的语法是否不起作用。
我在使用这个时遇到了一些奇怪的错误(例如使用 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]
}
Run Code Online (Sandbox Code Playgroud)
我进口implicits Numeric.IntIsIntegral,Numeric.DoubleIsFractional对应的范围,以避免歧义喜欢在不工作的隐式视图-是我隐高清惹的祸?
顺便说一句,您可以使用单个类型类来表达相同的逻辑,添加更多类型参数和条件隐式 ( listIsArray)
abstract class IsArray[A, T: Numeric, R, Out] {
def getRef(self: A, ref: R): Out
}
trait LowPriorityIsArray {
implicit def listIsArray[A, T: Numeric, R, Out](implicit
singleIsArray: IsArray[A, T, R, Out]
): IsArray[A, T, List[R], List[Out]] = new IsArray[A, T, List[R], List[Out]] {
override def getRef(self: A, ref: List[R]): List[Out] =
ref.map(singleIsArray.getRef(self, _))
}
}
object IsArray extends LowPriorityIsArray {
implicit val mcIsIntArray: IsArray[MyClass, Int, Int, Int] = new IsArray[MyClass, Int, Int, Int] {
override def getRef(self: MyClass, idx: Int): Int = self.is(idx)
}
implicit val mcIsDoubleArray: IsArray[MyClass, Double, Int, Double] = new IsArray[MyClass, Double, Int, Double] {
override def getRef(self: MyClass, idx: Int): Double = self.is(idx)
}
}
object IsArraySyntax {
implicit class IsArrayOps3[A, T: Numeric, R, Out](self: A) {
def getSingleElem(idx: Int)(implicit
isAr: IsArray[A, T, R, Out],
ev: Int <:< R
): Out = isAr.getRef(self, idx)
def getRef(ref: R)(implicit isAr: IsArray[A, T, R, Out]): Out =
isAr.getRef(self, ref)
}
}
case class MyClass(is: List[Int])
import IsArraySyntax._
{
import Numeric.IntIsIntegral
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
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]
}
Run Code Online (Sandbox Code Playgroud)
注意语法
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)
}
}
Run Code Online (Sandbox Code Playgroud)
不管用。
或者你可以创建Out一个类型成员而不是类型参数。
好吧,我刚刚注意到我实际上并没有T在我的类型类中使用,所以这可能不是你想要的。你可以尝试一个更高级的类型类
abstract class IsArray[A, T: Numeric, Col[_], R <: Col[T], Out] {
def getRef(self: A, ref: R): Out
}
Run Code Online (Sandbox Code Playgroud)
要不就
abstract class IsArray[A, T: Numeric, Col[_], Out] {
def getRef(self: A, ref: Col[T]): Out
}
Run Code Online (Sandbox Code Playgroud)
在我们的实例中,Col可以是Id或List。
| 归档时间: |
|
| 查看次数: |
92 次 |
| 最近记录: |