我正在尝试路径依赖类型,并且在尝试为它编写 scalaz.Equal实例时遇到了问题.我有以下结构: class A { class B }val a1 = new A val b1 = new a1.B // type a1.Bval a2 = new A val b2 = new a2.B //type a2.B 我首先想
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
所以,我想知道为什么===不起作用,如果我能编写一个适用于所有A#Bs的Equal实例?
我尝试了一种带有隐式转换的家庭酿造解决方案,它起作用
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中,你只为A#B定义一个Equal实例.这意味着即使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.
当你写一些(1)=== Some(1)vs. Option(1)=== Option(1)(或some(1)=== some(1))时,你可以看到同样的东西. Scalaz为选项[A:Equal]提供了Equal,但是对于Some [A:Equal]不提供,当第一个参数具有更具体的静态类型时,将找不到Option实例.
这不是你想要解决的问题,因为Scalaz的Equal的不变性是故意的.如果您希望在此上下文中将A#B值用作A#B值,则需要显式更新它们.