cercherò di descrivere i concetti dal punto di vista di un pedone casuale (non ho mai contribuito una sola riga alla biblioteca raccolta Scala, quindi non mi ha colpito troppo difficile se sbaglio).
Dato che LinkedList
è obsoleto, e poiché Maps fornisce un esempio migliore, userò TreeMap
come esempio.
CanBuildFrom
La motivazione è questo: se prendiamo un TreeMap[Int, Int]
e mappa con
case (x, y) => (2 * x, y * y * 0.3d)
otteniamo TreeMap[Int, Double]
. Questo tipo di sicurezza da solo spiegherebbe già la necessità dei costrutti semplici genericBuilder[X]
. Tuttavia, se mapparla con
case (x, y) => x
si ottiene un Iterable[Int]
(più precisamente: un List[Int]
), questo non è più una mappa, il tipo di contenitore è cambiato. Questo è dove CBF del entrano in gioco:
CanBuildFrom[This, X, That]
può essere visto come una sorta di "funzione di tipo-level" che ci dice: se tracciamo una collezione di tipo questo con una funzione che restituisce valori di tipo X , possiamo costruire un That. La più specifica CBF è previsto al momento della compilazione, nel primo caso sarà qualcosa di simile a
CanBuildFrom[TreeMap[_,_], (X,Y), TreeMap[X,Y]]
nel secondo caso sarà qualcosa di simile a
CanBuildFrom[TreeMap[_,_], X, Iterable[X]]
e così abbiamo sempre ottenere la giusta tipo del contenitore. Il modello è piuttosto generale. Ogni volta che si dispone di una funzione generica
foo[X1, ..., Xn](x1: X1, ..., xn: Xn): Y
in cui il tipo di risultato Y dipende X1, ..., Xn, si può introdurre un parametro implicito come segue:
foo[X1, ...., Xn, Y](x1: X1, ..., xn: Xn)(implicit CanFooFrom[X1, ..., Xn, Y]): Y
e quindi definire la funzione a livello di testo X1, ..., Xn -> Y a tratti fornendo più CanfooFrom di implicito.
LinkedListLike
nella definizione della classe, si vede qualcosa di simile:
TreeMap[A, B] extends SortedMap[A, B] with SortedMapLike[A, B, TreeMap[A, B]]
Questo è il modo di Scala per esprimere il cosiddetto polimorfismo F-delimitata. La motivazione è la seguente: Supponiamo di avere una dozzina (o almeno due ...) implementazioni del tratto SortedMap[A, B]
. Ora vogliamo implementare un metodo withoutHead
, potrebbe sembrare un po 'come questo:
def withoutHead = this.remove(this.head)
Se ci spostiamo l'attuazione in SortedMap[A, B]
stesso, il meglio che possiamo fare è questo:
def withoutHead: SortedMap[A, B] = this.remove(this.head)
Ma è questo è il tipo di risultato più specifico che possiamo ottenere? No, è troppo vago. Vorremmo restituire TreeMap[A, B]
se la mappa originale è TreeMap
e CrazySortedLinkedHashMap
(o qualsiasi altra cosa ...) se l'originale era un CrazySortedLinkedHashMap
. È per questo che ci muoviamo l'attuazione in SortedMapLike
, e diamo la seguente firma per il metodo withoutHead
:
trait SortedMapLike[A, B, Repr <: SortedMap[A, B]] {
...
def withoutHead: Repr = this.remove(this.head)
}
ora perché TreeMap[A, B]
estende SortedMapLike[A, B, TreeMap[A, B]]
, il tipo di risultato di withoutHead
è TreeMap[A,B]
. Lo stesso vale per CrazySortedLinkedHashMap
: otteniamo il tipo esatto indietro.In Java, si dovrebbe attuare una politica tornare SortedMap[A, B]
o l'override del metodo in ogni sottoclasse (che si è rivelato essere un incubo di manutenzione per i tratti ricchi di funzionalità a Scala)
GenericTraversableTemplate
Il tipo è: GenericTraversableTemplate[+A, +CC[X] <: GenTraversable[X]]
Per quanto posso dire, questo è solo una caratteristica che fornisce implementazioni di metodi che restituiscono qualche modo collezioni regolari con lo stesso tipo di contenitore, ma possibilmente diverso tipo di contenuto (roba come flatten
, transpose
, unzip
).
Stuff come foldLeft
, reduce
, exists
non sono qui perché questi metodi riguardano solo il tipo di contenuto, non il tipo di contenitore.
Stuff come flatMap
non è qui, perché il tipo di contenitore può cambiare (di nuovo, CBF).
Perché è un tratto separato, esiste una ragione fondamentale per cui esiste? Io non la penso così ... Probabilmente sarebbe possibile raggruppare la godzillion dei metodi in qualche modo in modo diverso. Ma questo è proprio ciò che accade in modo naturale: si inizia a implementare un tratto e si scopre che ha molti metodi. Così, invece di raggruppare metodi vagamente correlati, e metterli in 10 diversi tratti con nomi scomodi come "GenTraversableTemplate", e li li mescolano in tratti/classi dove ne avete bisogno ...
GenericCompanion
questa è solo una classe astratta che implementa alcune funzionalità di base che è comune per gli oggetti compagno della maggior parte delle classi di raccolta (in sostanza, solo che implementa molto semplici metodi di fabbrica apply(varargs)
e empty
).
Per esempio c'è il metodo apply
che prende varargs
di qualche tipo A e restituisce un insieme di tipo CC[A]
:
Array(1, 2, 3, 4) // calls Array.apply[A](elems: A*) on the companion object
List(1, 2, 3, 4) // same for List
L'implementazione è molto semplice, si tratta di qualcosa di simile:
def apply[A](varargs: A*): CC[A] = {
val builder = newBuilder[A]
for (arg <- varargs) builder += arg
builder.result()
}
Ovviamente è lo stesso per Array e Liste e TreeMaps e quasi tutto il resto, eccetto "Collezioni irregolari vincolate" come Bitset. Quindi questa è solo una funzionalità comune in una classe di antenati comune della maggior parte degli oggetti associati. Niente di speciale in questo.
SeqFactory
Simile a GenericCompanion, ma questa volta più specificamente per le sequenze. aggiunge alcuni metodi di fabbrica comuni come fill()
e iterate()
e tabulate()
ecc Anche in questo caso, niente di particolarmente razzo-scientifica qui ...
osservazioni generali Pochi
In generale: non credo che si dovrebbe cercare di capire ogni singolo tratto in questa biblioteca. Piuttosto, si dovrebbe cercare di guardare la biblioteca nel suo insieme. Nel complesso, ha un'architettura molto interessante. E secondo la mia personale opinione, è in realtà un software molto estetico, ma bisogna guardarlo per un po '(e provare a ri-implementare l'intero modello architettonico più volte) per afferrarlo. D'altra parte: per esempio i CBF sono una specie di "modello di progettazione" che chiaramente dovrebbe essere eliminato nei successori di questa lingua. L'intera storia con lo scopo implicito di CBF sembra ancora un incubo per me. Ma all'inizio molte cose sembravano completamente imperscrutabili, e quasi sempre finivano con un'epifania (che è molto specifica per Scala: per la maggior parte delle altre lingue, queste lotte di solito finiscono con il pensiero "Autore di questo è un completo idiota") .
[We're Do It All Wrong] (http://youtu.be/TS1lpKBMkgg?t=14m26s) –
Sì, e la versione leggermente modificata: [Scala Collections: Why Not?] (Https: // www .youtube.com/watch? v = uiJycy6dFSQ) –