Divido os operadores, para fins de ensino, em quatro categorias :
- Palavras-chave / símbolos reservados
- Métodos importados automaticamente
- Métodos comuns
- Açúcares sintáticos / composição
É uma sorte, então, que a maioria das categorias esteja representada na pergunta:
-> // 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
O significado exato da maioria desses métodos depende da classe que os está definindo. Por exemplo, <=
on Int
significa "menor ou igual a" . O primeiro, ->
vou dar como exemplo abaixo. ::
é provavelmente o método definido em List
(embora possa ser o objeto com o mesmo nome) e :+=
provavelmente é o método definido em várias Buffer
classes.
Então, vamos vê-los.
Palavras-chave / símbolos reservados
Existem alguns símbolos em Scala que são especiais. Dois deles são considerados palavras-chave adequadas, enquanto outros são apenas "reservados". Eles são:
// 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
Tudo isso faz parte do idioma e, como tal, pode ser encontrado em qualquer texto que descreva adequadamente o idioma, como o próprio Scala Specification (PDF).
O último, o sublinhado, merece uma descrição especial, porque é amplamente utilizada e tem muitos significados diferentes. Aqui está uma amostra:
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
Eu provavelmente esqueci outro significado, no entanto.
Métodos importados automaticamente
Portanto, se você não encontrou o símbolo que está procurando na lista acima, ele deve ser um método ou parte dele. Mas, com frequência, você verá algum símbolo e a documentação da classe não terá esse método. Quando isso acontece, você está visualizando uma composição de um ou mais métodos com outra coisa ou o método foi importado para o escopo ou está disponível por meio de uma conversão implícita importada.
Eles ainda podem ser encontrados no ScalaDoc : você apenas precisa saber onde procurá-los. Ou, na sua falta, veja o índice (atualmente quebrado em 2.9.1, mas disponível todas as noites).
Todo código Scala tem três importações automáticas:
// Not necessarily in this order
import _root_.java.lang._ // _root_ denotes an absolute path
import _root_.scala._
import _root_.scala.Predef._
Os dois primeiros disponibilizam apenas classes e objetos singleton. O terceiro contém todas as conversões implícitas e métodos importados, pois Predef
é um objeto em si.
Olhando para dentro, Predef
mostre rapidamente alguns símbolos:
class <:<
class =:=
object <%<
object =:=
Qualquer outro símbolo será disponibilizado através de uma conversão implícita . Basta olhar para os métodos marcados com implicit
que recebem, como parâmetro, um objeto do tipo que está recebendo o método. Por exemplo:
"a" -> 1 // Look for an implicit from String, AnyRef, Any or type parameter
No caso acima, ->
é definido na classe ArrowAssoc
através do método any2ArrowAssoc
que leva um objeto do tipo A
, em que A
é um parâmetro do tipo ilimitado para o mesmo método.
Métodos comuns
Portanto, muitos símbolos são simplesmente métodos em uma classe. Por exemplo, se você fizer
List(1, 2) ++ List(3, 4)
Você encontrará o método ++
no ScalaDoc for List . No entanto, há uma convenção que você deve estar ciente ao procurar métodos. Os métodos que terminam em dois pontos ( :
) se ligam à direita em vez da esquerda. Em outras palavras, enquanto a chamada do método acima é equivalente a:
List(1, 2).++(List(3, 4))
Se eu tivesse, 1 :: List(2, 3)
isso seria equivalente a:
List(2, 3).::(1)
Então, você precisa olhar para o tipo encontrado à direita ao procurar métodos que terminam em dois pontos. Considere, por exemplo:
1 +: List(2, 3) :+ 4
O primeiro método ( +:
) se liga à direita e é encontrado em List
. O segundo método ( :+
) é apenas um método normal e se liga à esquerda - novamente, ativado List
.
Açúcares sintáticos / composição
Então, aqui estão alguns açúcares sintáticos que podem ocultar um 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
O último é interessante, porque qualquer método simbólico pode ser combinado para formar um método semelhante a uma atribuição dessa maneira.
E, claro, existem várias combinações que podem aparecer no 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.