Divido gli operatori, ai fini dell'insegnamento, in quattro categorie :
- Parole chiave / simboli riservati
- Metodi importati automaticamente
- Metodi comuni
- Zuccheri sintattici / composizione
È una fortuna, quindi, che la maggior parte delle categorie siano rappresentate nella domanda:
-> // Automatically imported method
||= // Syntactic sugar
++= // Syntactic sugar/composition or common method
<= // Common method
_._ // Typo, though it's probably based on Keyword/composition
:: // Common method
:+= // Common method
Il significato esatto della maggior parte di questi metodi dipende dalla classe che li sta definendo. Ad esempio, <=
on Int
significa "minore o uguale a" . Il primo, ->
ti darò come esempio di seguito. ::
è probabilmente il metodo definito List
(anche se potrebbe essere l'oggetto con lo stesso nome) ed :+=
è probabilmente il metodo definito su varie Buffer
classi.
Quindi, vediamoli.
Parole chiave / simboli riservati
Ci sono alcuni simboli in Scala che sono speciali. Due di questi sono considerati parole chiave appropriate, mentre altri sono semplicemente "riservati". Loro sono:
// Keywords
<- // Used on for-comprehensions, to separate pattern from generator
=> // Used for function types, function literals and import renaming
// Reserved
( ) // Delimit expressions and parameters
[ ] // Delimit type parameters
{ } // Delimit blocks
. // Method call and path separator
// /* */ // Comments
# // Used in type notations
: // Type ascription or context bounds
<: >: <% // Upper, lower and view bounds
<? <! // Start token for various XML elements
" """ // Strings
' // Indicate symbols and characters
@ // Annotations and variable binding on pattern matching
` // Denote constant or enable arbitrary identifiers
, // Parameter separator
; // Statement separator
_* // vararg expansion
_ // Many different meanings
Questi fanno tutti parte della lingua e, come tali, possono essere trovati in qualsiasi testo che descriva correttamente la lingua, come la specifica Scala (PDF) stessa.
L'ultimo, il carattere di sottolineatura, merita una descrizione speciale, perché è così ampiamente usato e ha tanti significati diversi. Ecco un esempio:
import scala._ // Wild card -- all of Scala is imported
import scala.{ Predef => _, _ } // Exception, everything except Predef
def f[M[_]] // Higher kinded type parameter
def f(m: M[_]) // Existential type
_ + _ // Anonymous function placeholder parameter
m _ // Eta expansion of method into method value
m(_) // Partial function application
_ => 5 // Discarded parameter
case _ => // Wild card pattern -- matches anything
f(xs: _*) // Sequence xs is passed as multiple parameters to f(ys: T*)
case Seq(xs @ _*) // Identifier xs is bound to the whole matched sequence
Probabilmente ho dimenticato qualche altro significato, però.
Metodi importati automaticamente
Quindi, se non hai trovato il simbolo che stai cercando nell'elenco sopra, allora deve essere un metodo o parte di uno. Ma spesso vedrai alcuni simboli e la documentazione per la classe non avrà quel metodo. Quando ciò accade, stai osservando una composizione di uno o più metodi con qualcos'altro, oppure il metodo è stato importato nell'ambito o è disponibile tramite una conversione implicita importata.
Questi possono ancora essere trovati su ScalaDoc : devi solo sapere dove cercarli. Oppure, in mancanza di ciò, guarda l' indice (attualmente rotto su 2.9.1, ma disponibile di notte).
Ogni codice Scala ha tre importazioni automatiche:
// Not necessarily in this order
import _root_.java.lang._ // _root_ denotes an absolute path
import _root_.scala._
import _root_.scala.Predef._
I primi due rendono disponibili solo le classi e gli oggetti singleton. Il terzo contiene tutte le conversioni implicite e i metodi importati, poiché Predef
è un oggetto stesso.
Guardando dentro Predef
mostra rapidamente alcuni simboli:
class <:<
class =:=
object <%<
object =:=
Qualsiasi altro simbolo sarà reso disponibile attraverso una conversione implicita . Basta guardare i metodi taggati con implicit
quel ricevere, come parametro, un oggetto di tipo che sta ricevendo il metodo. Per esempio:
"a" -> 1 // Look for an implicit from String, AnyRef, Any or type parameter
Nel caso precedente, ->
viene definito nella classe ArrowAssoc
tramite il metodo any2ArrowAssoc
che accetta un oggetto di tipo A
, dove A
è un parametro di tipo non associato allo stesso metodo.
Metodi comuni
Quindi, molti simboli sono semplicemente metodi su una classe. Ad esempio, se lo fai
List(1, 2) ++ List(3, 4)
Troverai il metodo ++
direttamente su ScalaDoc for List . Tuttavia, c'è una convenzione che devi conoscere quando cerchi i metodi. I metodi che terminano con due punti ( :
) si legano a destra anziché a sinistra. In altre parole, mentre la chiamata del metodo sopra è equivalente a:
List(1, 2).++(List(3, 4))
Se avessi, invece 1 :: List(2, 3)
, sarebbe equivalente a:
List(2, 3).::(1)
Quindi devi guardare il tipo trovato a destra quando cerchi i metodi che terminano con i due punti. Considera, ad esempio:
1 +: List(2, 3) :+ 4
Il primo metodo ( +:
) si lega a destra e si trova su List
. Il secondo metodo ( :+
) è solo un metodo normale e si lega a sinistra - di nuovo, su List
.
Zuccheri sintattici / composizione
Quindi, ecco alcuni zuccheri sintattici che potrebbero nascondere un metodo:
class Example(arr: Array[Int] = Array.fill(5)(0)) {
def apply(n: Int) = arr(n)
def update(n: Int, v: Int) = arr(n) = v
def a = arr(0); def a_=(v: Int) = arr(0) = v
def b = arr(1); def b_=(v: Int) = arr(1) = v
def c = arr(2); def c_=(v: Int) = arr(2) = v
def d = arr(3); def d_=(v: Int) = arr(3) = v
def e = arr(4); def e_=(v: Int) = arr(4) = v
def +(v: Int) = new Example(arr map (_ + v))
def unapply(n: Int) = if (arr.indices contains n) Some(arr(n)) else None
}
val Ex = new Example // or var for the last example
println(Ex(0)) // calls apply(0)
Ex(0) = 2 // calls update(0, 2)
Ex.b = 3 // calls b_=(3)
// This requires Ex to be a "val"
val Ex(c) = 2 // calls unapply(2) and assigns result to c
// This requires Ex to be a "var"
Ex += 1 // substituted for Ex = Ex + 1
L'ultimo è interessante, perché qualsiasi metodo simbolico può essere combinato per formare in questo modo un metodo simile a un incarico.
E, naturalmente, ci sono varie combinazioni che possono apparire nel codice:
(_+_) // An expression, or parameter, that is an anonymous function with
// two parameters, used exactly where the underscores appear, and
// which calls the "+" method on the first parameter passing the
// second parameter as argument.