2015-05-20 14 views
5

Sto sperimentando tipi dipendenti dal percorso e ho riscontrato un problema durante il tentativo di scrivere un'istanza scalaz.Equal per questo. Ho la seguente struttura:scalaz.Equal per tipi dipendenti dal percorso

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 

primo luogo ho voluto fare b1 "unequalable" (che è una parola?) Per b2 al momento della compilazione, che ho raggiunto con il seguente:

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 

mio secondo esperimento è stato cercare di rendere l'uguaglianza meno restrittive, consentendo istanze di A#B da confrontare tra di loro, ma non ad altri tipi, con:

implicit val BEqual: scalaz.Equal[A#B] = Equal.equalA 

Ma non funziona come previsto:

b1 === b2 //doesnt' compile, === is not a member of a1.B 

Questo funziona però:

BEqual.equal(b1,b2) //compiles 
BEqual.equal(b1,"string") //doesnt' compile, good 

Quindi, vorrei sapere perché il === non funziona e se posso scrivere un caso di Equal applicabile a tutti gli A#B s?

Ho provato una soluzione di birra fatta in casa con conversione implicita e ha funzionato.

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 

Quindi, perché questo non funziona con scalaz.Equal?

risposta

4

Nel tuo primo BEqual stai dicendo che per qualsiasi sottotipo di A#B si desidera fornire un esempio Equalper questo sottotipo. Quando il compilatore vede b1 === trova quindi l'istanza Equal[a.B], poiché il tipo statico di b1 è a.B. Questo fa funzionare le cose come ti aspetteresti.

Nel secondo BEqual, si sta definendo un'istanza Equal solo per A#B. Ciò significa che anche b1 === b1 non verrà compilato, poiché il tipo statico di b1 è più specifico di A#B e Equal è invariato nel suo parametro tipo. Se i valori upcast l'istanza funzionano bene:

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 

Nella versione in cui si chiama BEqual.equal direttamente, si sta compiendo in sostanza la stessa cosa per il metodo argomenti sono sempre covarianti, in modo che quando si passa qualcosa di statico digitato come a.B come argomento A#B, tutto funzionerà perfettamente. Nella tua classe implicita a mano, stai dicendo semplicemente che vuoi lavorare con qualsiasi vecchio A#B.

È possibile vedere lo stesso tipo di cosa quando si scrive Some(1) === Some(1) rispetto a Option(1) === Option(1) (o some(1) === some(1)). Scalaz fornisce Equal per Option[A: Equal], ma non per Some[A: Equal], e quando il primo argomento ha un tipo statico più specifico, l'istanza Option non verrà trovata.

Questo non è qualcosa che si vuole aggirare, poiché l'invarianza di Equal di Scalaz è intenzionale. Se si desidera lavorare con valori A#B come valori A#B in questo contesto, è necessario eseguire l'upcast in modo esplicito.

+1

"e' Equal' è invariante nel suo parametro tipo ". Proprio lì, quello era il dettaglio che trascuravo! Sono d'accordo che non voglio lavorarci sopra, è il comportamento corretto! Grazie! – Chirlo

Problemi correlati