scalaz.Equal用于路径依赖类型

时间:2015-05-20 12:09:47

标签: scala scalaz path-dependent-type

我尝试使用与路径相关的类型,并且在尝试为其编写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的{​​{1}}实例?

我尝试了一种带有隐式转换的家庭酿造解决方案,但它确实有效。

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 无关?

1 个答案:

答案 0 :(得分:4)

在您的第一个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静态输入的内容作为{{1}传递时参数,一切都会好起来的。在您的手动隐式类中,您同样只是说您想要使用任何旧的A#B

当您撰写A#BSome(1) === Some(1)(或Option(1) === Option(1))时,您可以看到同样的事情。 Scalaz为some(1) === some(1)提供Equal,但不为Option[A: Equal]提供,当第一个参数具有更具体的静态类型时,将找不到Some[A: Equal]实例。

这不是你想要解决的问题,因为Scalaz Option的不变性是故意的。如果您希望在此上下文中使用Equal值作为A#B值,则需要明确地重新定位它们。