Divido a los operadores, con el propósito de enseñar, en cuatro categorías :
- Palabras clave / símbolos reservados
- Métodos importados automáticamente
- Métodos comunes
- Azúcares sintácticos / composición
Es una suerte, entonces, que la mayoría de las categorías estén representadas en la pregunta:
-> // 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
El significado exacto de la mayoría de estos métodos depende de la clase que los define. Por ejemplo, <=
on Int
significa "menor o igual que" . El primero, ->
daré como ejemplo a continuación. ::
es probablemente el método definido en List
(aunque podría ser el objeto del mismo nombre), y :+=
es probablemente el método definido en varias Buffer
clases.
Entonces, vamos a verlos.
Palabras clave / símbolos reservados
Hay algunos símbolos en Scala que son especiales. Dos de ellas se consideran palabras clave adecuadas, mientras que otras simplemente están "reservadas". Son:
// 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
Todos estos son parte del lenguaje y, como tal, se pueden encontrar en cualquier texto que describa adecuadamente el idioma, como la propia Especificación de Scala (PDF).
El último, el guión bajo, merece una descripción especial, porque es muy utilizado y tiene muchos significados diferentes. Aquí hay una muestra:
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
Sin embargo, probablemente olvidé algún otro significado.
Métodos importados automáticamente
Por lo tanto, si no encontró el símbolo que busca en la lista anterior, debe ser un método o parte de uno. Pero, a menudo, verá algún símbolo y la documentación de la clase no tendrá ese método. Cuando esto sucede, está viendo una composición de uno o más métodos con otra cosa, o el método se ha importado al alcance o está disponible a través de una conversión implícita importada.
Estos todavía se pueden encontrar en ScalaDoc : sólo hay que saber dónde buscar para ellos. O, en su defecto, mire el índice (actualmente roto en 2.9.1, pero disponible en la noche).
Cada código Scala tiene tres importaciones automáticas:
// Not necessarily in this order
import _root_.java.lang._ // _root_ denotes an absolute path
import _root_.scala._
import _root_.scala.Predef._
Los dos primeros solo hacen que las clases y los objetos singleton estén disponibles. El tercero contiene todas las conversiones implícitas y métodos importados, ya que Predef
es un objeto en sí mismo.
Mirando hacia dentro Predef
rápidamente muestra algunos símbolos:
class <:<
class =:=
object <%<
object =:=
Cualquier otro símbolo estará disponible a través de una conversión implícita . Solo mire los métodos etiquetados con implicit
esa recepción, como parámetro, un objeto de tipo que está recibiendo el método. Por ejemplo:
"a" -> 1 // Look for an implicit from String, AnyRef, Any or type parameter
En el caso anterior, ->
se define en la clase a ArrowAssoc
través del método any2ArrowAssoc
que toma un objeto de tipo A
, donde A
es un parámetro de tipo ilimitado para el mismo método.
Métodos comunes
Entonces, muchos símbolos son simplemente métodos en una clase. Por ejemplo, si lo haces
List(1, 2) ++ List(3, 4)
Encontrará el método ++
directamente en ScalaDoc for List . Sin embargo, hay una convención que debe tener en cuenta al buscar métodos. Los métodos que terminan en dos puntos ( :
) se unen a la derecha en lugar de a la izquierda. En otras palabras, mientras que la llamada al método anterior es equivalente a:
List(1, 2).++(List(3, 4))
Si tuviera, en cambio 1 :: List(2, 3)
, eso sería equivalente a:
List(2, 3).::(1)
Por lo tanto, debe buscar el tipo que se encuentra a la derecha cuando busque métodos que terminen en dos puntos. Considere, por ejemplo:
1 +: List(2, 3) :+ 4
El primer método ( +:
) se une a la derecha y se encuentra en List
. El segundo método ( :+
) es solo un método normal y se une a la izquierda, nuevamente, encendido List
.
Azúcares sintácticos / composición
Entonces, aquí hay algunos azúcares sintácticos que pueden ocultar un método:
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
El último es interesante, porque cualquier método simbólico se puede combinar para formar un método de asignación de esa manera.
Y, por supuesto, hay varias combinaciones que pueden aparecer en el código:
(_+_) // 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.