A mio parere, i due più semplici esempi dopo Function
sono ordinare e l'uguaglianza. Tuttavia, il primo non è contro-variante nella libreria standard di Scala, e il secondo non esiste nemmeno in esso. Quindi, userò gli equivalenti Scalaz: Order e Equal.
Successivamente, ho bisogno di una gerarchia di classi, preferibilmente una che sia familiare e, ovviamente, entrambi i concetti di cui sopra devono avere senso per questo. Se Scala avesse una superclasse Number
di tutti i tipi numerici, sarebbe stato perfetto. Sfortunatamente, non ha niente del genere.
Così ho intenzione di provare a rendere gli esempi con le collezioni. Per semplificare, consideriamo solo Seq[Int]
e List[Int]
. Dovrebbe essere chiaro che List[Int]
è un sottotipo di Seq[Int]
, ovvero List[Int] <: Seq[Int]
.
Quindi, cosa possiamo fare con esso? In primo luogo, cerchiamo di scrivere qualcosa che confronta due liste:
def smaller(a: List[Int], b: List[Int])(implicit ord: Order[List[Int]]) =
if (ord.order(a,b) == LT) a else b
Ora ho intenzione di scrivere un implicito Order
per Seq[Int]
:
implicit val seqOrder = new Order[Seq[Int]] {
def order(a: Seq[Int], b: Seq[Int]) =
if (a.size < b.size) LT
else if (b.size < a.size) GT
else EQ
}
Con queste definizioni, ora posso fare qualcosa di simile:
scala> smaller(List(1), List(1, 2, 3))
res0: List[Int] = List(1)
Nota che sto chiedendo una Order[List[Int]]
, ma sto passando un Order[Seq[Int]]
. Ciò significa che Order[Seq[Int]] <: Order[List[Int]]
. Dato che Seq[Int] >: List[Int]
, questo è possibile solo a causa della contro-varianza.
La prossima domanda è: ha senso?
Consideriamo nuovamente smaller
. Voglio confrontare due liste di numeri interi. Naturalmente, tutto ciò che confronta due elenchi è accettabile, ma qual è la logica di qualcosa che mette a confronto due Seq[Int]
accettabili?
Nota nella definizione di seqOrder
in che modo gli oggetti confrontati diventano parametri. Ovviamente, un List[Int]
può essere un parametro per qualcosa che si aspetta un Seq[Int]
. Ne consegue che qualcosa che confronta Seq[Int]
è accettabile al posto di qualcosa che confronta List[Int]
: entrambi possono essere utilizzati con gli stessi parametri.
E il contrario? Diciamo che ho avuto un metodo che ha solo confrontato ::
(elenco di svantaggi), che, insieme a Nil
, è un sottotipo di List
. Ovviamente non potrei usare questo, perché smaller
potrebbe ricevere uno Nil
da confrontare. Ne consegue che non è possibile utilizzare Order[::[Int]]
anziché Order[List[Int]]
.
Procediamo all'uguaglianza, e scrivere un metodo per esso:
def equalLists(a: List[Int], b: List[Int])(implicit eq: Equal[List[Int]]) = eq.equal(a, b)
Poiché Order
estende Equal
, posso usare con lo stesso implicito sopra:
scala> equalLists(List(4, 5, 6), List(1, 2, 3)) // we are comparing lengths!
res3: Boolean = true
La logica è il lo stesso. Tutto ciò che può dire se due Seq[Int]
sono uguali può, ovviamente, anche dire se due List[Int]
sono uguali. Da ciò segue che Equal[Seq[Int]] <: Equal[List[Int]]
, che è vero perché Equal
è contro-variante.
Vedere la risposta di Daniel Spiewak a http: // stackoverflow.it/questions/663254/scala-covariance-contravariance-question – sourcedelica
... perché nessuno usa funzioni nel mondo reale? =) –