2015-05-20 1 views
5

Я экспериментирую с зависимыми от пути типами, и я сталкиваюсь с проблемой при попытке написать экземпляр scalaz.Equal для него. У меня есть следующая структура: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 «unequalable» (это то, что слово?), Чтобы 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 s?

Я попробовал решение для домашнего приготовления с неявным преобразованием, и это сработало.

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?

ответ

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 = [email protected] 

scala> val ab2: A#B = b2 
ab2: A#B = [email protected] 

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's Equal преднамеренно. Если вы хотите работать с значениями A#B как значениями A#B в этом контексте, вам нужно будет их явно увеличить.

+1

"и' Equal' является инвариантным по параметру своего типа. Прямо там, это была деталь, которую я забыл! Я согласен, что я не хочу работать, это правильное поведение! Благодаря! – Chirlo

 Смежные вопросы

  • Нет связанных вопросов^_^