我正在尝试依赖路径的类型,但在尝试编写 scalaz.Equal
时遇到了问题。例如。我有以下结构:
class A {
class B
}
val a1 = new A
val b1 = new a1.B // type a1.B
val a2 = new A
val b2 = new a2.B //type a2.B
我首先想做
b1
“无与伦比”(这是一个词吗?)到 b2
在编译时,我通过以下方式实现了:import scalaz._
import Scalaz._
implicit def BEqual[X <: A#B]: scalaz.Equal[X] = Equal.equalA
b1 === b1 //true
b1 === b2 // doesn't compile, good
b2 === b1 // doesn't compile, good
我的第二个实验是尝试减少对等式的限制,允许
A#B
的实例。相互比较,但不与其他类型进行比较,具有: implicit val BEqual: scalaz.Equal[A#B] = Equal.equalA
但它没有按预期工作:
b1 === b2 //doesnt' compile, === is not a member of a1.B
然而,这有效:
BEqual.equal(b1,b2) //compiles
BEqual.equal(b1,"string") //doesnt' compile, good
所以,我想知道为什么
===
不起作用,如果我可以编写 Equal
的实例这将适用于所有 A#B
?我尝试了一个带有隐式转换的自制解决方案,它奏效了。
implicit class abEqual(ab: A#B) {
def eqab(ab2: A#B) = ab == ab2
}
b1.eqab(b2) //ok
b2.eqab(b1) //ok
b1.eqab("String") //doesn't compile, good
那么为什么这不适用于
scalaz.Equal
?
最佳答案
在您的第一个 BEqual
你是说对于 A#B
的任何子类型您想提供一个 Equal
该子类型的实例。当编译器看到 b1 ===
因此,它找到了 Equal[a.B]
例如,由于 b1
的静态类型是 a.B
.这使事情如您所愿。
在您的第二个 BEqual
,您正在定义一个 Equal
实例仅适用于 A#B
.这意味着即使 b1 === b1
不会编译,因为 b1
的静态类型比 A#B
更具体, 和 Equal
它的类型参数是不变的。如果您向上转换您的值,该实例将正常工作:
scala> val ab1: A#B = b1
ab1: A#B = A$B@464ef4fa
scala> val ab2: A#B = b2
ab2: A#B = A$B@2d3b749e
scala> ab1 === ab2
res1: Boolean = false
在你调用的版本
BEqual.equal
直接,你基本上完成了同样的事情——方法参数总是协变的,所以当你传递静态类型为 a.B
的东西时作为 A#B
争论,一切都会好起来的。在你手动滚动的隐式类中,你同样只是说你想使用任何旧的 A#B
.写
Some(1) === Some(1)
也能看到类似的东西对比 Option(1) === Option(1)
(或 some(1) === some(1)
)。 Scalaz 提供了一个 Equal
为 Option[A: Equal]
,但不适用于 Some[A: Equal]
,并且当第一个参数具有更具体的静态类型时, Option
找不到实例。这不是您想要解决的问题,因为 Scalaz 的不变性
Equal
是故意的。如果您想与 A#B
合作值为 A#B
在这种情况下,您需要明确地向上转换它们。
关于scalaz.Equal 用于路径依赖类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30349256/