Ho provato una volta a scrivere di questo, ma alla fine mi sono arreso, poiché le regole sono piuttosto diffuse. Fondamentalmente, dovrai prenderne il controllo.
Forse è meglio concentrarsi su dove parentesi graffe e parentesi possono essere usate in modo intercambiabile: quando si passano i parametri alle chiamate di metodo. È possibile sostituire la parentesi con parentesi graffe se, e solo se, il metodo prevede un singolo parametro. Per esempio:
List(1, 2, 3).reduceLeft{_ + _} // valid, single Function2[Int,Int] parameter
List{1, 2, 3}.reduceLeft(_ + _) // invalid, A* vararg parameter
Tuttavia, c'è altro che devi sapere per capire meglio queste regole.
Maggiore controllo della compilazione con parentesi
Gli autori di Spray raccomandano parentesi rotonde perché offrono un maggiore controllo di compilazione. Ciò è particolarmente importante per i DSL come Spray. Usando le parentesi stai dicendo al compilatore che dovrebbe essere data una sola riga; quindi se gli dai accidentalmente due o più, si lamenterà. Ora questo non è il caso delle parentesi graffe: se per esempio dimentichi un operatore da qualche parte, il tuo codice verrà compilato e otterrai risultati imprevisti e potenzialmente un bug molto difficile da trovare. Di seguito è inventato (poiché le espressioni sono pure e daranno almeno un avvertimento), ma sottolinea il punto:
method {
1 +
2
3
}
method(
1 +
2
3
)
Il primo compila, il secondo dà error: ')' expected but integer literal found
. L'autore voleva scrivere 1 + 2 + 3
.
Si potrebbe sostenere che è simile per i metodi multiparametrici con argomenti predefiniti; è impossibile dimenticare accidentalmente una virgola per separare i parametri quando si usano le parentesi.
Verbosità
Una nota importante spesso trascurata sulla verbosità. L'uso delle parentesi graffe porta inevitabilmente a un codice dettagliato poiché la guida di stile Scala afferma chiaramente che la chiusura delle parentesi graffe deve essere sulla loro stessa linea:
... la parentesi graffa di chiusura si trova sulla propria riga immediatamente dopo l'ultima riga della funzione.
Molti auto-riformatori, come in IntelliJ, eseguiranno automaticamente questa riformattazione per te. Quindi cerca di continuare a usare le parentesi rotonde quando puoi.
Notazione Infix
Quando si utilizza la notazione infissa, come List(1,2,3) indexOf (2)
è possibile omettere la parentesi se esiste un solo parametro e scriverlo come List(1, 2, 3) indexOf 2
. Questo non è il caso della notazione a punti.
Si noti inoltre che quando si dispone di un singolo parametro che è un'espressione multi-token, come x + 2
o a => a % 2 == 0
, è necessario utilizzare la parentesi per indicare i limiti dell'espressione.
Le tuple
Poiché a volte puoi omettere la parentesi, a volte una tupla ha bisogno di parentesi extra come in ((1, 2))
, e talvolta la parentesi esterna può essere omessa, come in (1, 2)
. Ciò può causare confusione.
Letterali funzione / funzione parziale con case
Scala ha una sintassi per letterali di funzione e funzione parziale. Sembra così:
{
case pattern if guard => statements
case pattern => statements
}
Gli unici altri luoghi in cui è possibile utilizzare le case
istruzioni sono con le parole chiave match
e catch
:
object match {
case pattern if guard => statements
case pattern => statements
}
try {
block
} catch {
case pattern if guard => statements
case pattern => statements
} finally {
block
}
Non è possibile utilizzare le case
istruzioni in nessun altro contesto . Quindi, se vuoi usare case
, hai bisogno di parentesi graffe. Nel caso ti stia chiedendo cosa renda letterale la distinzione tra funzione e funzione parziale, la risposta è: contesto. Se Scala prevede una funzione, una funzione che ottieni. Se si aspetta una funzione parziale, si ottiene una funzione parziale. Se sono previsti entrambi, viene visualizzato un errore sull'ambiguità.
Espressioni e blocchi
Le parentesi possono essere utilizzate per creare sottoespressioni. Le parentesi graffe possono essere utilizzate per creare blocchi di codice (questa non è una funzione letterale, quindi fai attenzione a provare a usarla come una). Un blocco di codice è costituito da più istruzioni, ciascuna delle quali può essere un'istruzione di importazione, una dichiarazione o un'espressione. Va così:
{
import stuff._
statement ; // ; optional at the end of the line
statement ; statement // not optional here
var x = 0 // declaration
while (x < 10) { x += 1 } // stuff
(x % 5) + 1 // expression
}
( expression )
Quindi, se hai bisogno di dichiarazioni, dichiarazioni multiple, import
o qualcosa del genere, hai bisogno di parentesi graffe. E poiché un'espressione è un'affermazione, la parentesi può apparire tra parentesi graffe. Ma la cosa interessante è che i blocchi di codice sono anche espressioni, quindi puoi usarli ovunque all'interno di un'espressione:
( { var x = 0; while (x < 10) { x += 1}; x } % 5) + 1
Quindi, poiché le espressioni sono dichiarazioni e blocchi di codici sono espressioni, tutto ciò che segue è valido:
1 // literal
(1) // expression
{1} // block of code
({1}) // expression with a block of code
{(1)} // block of code with an expression
({(1)}) // you get the drift...
Dove non sono intercambiabili
Fondamentalmente, non è possibile sostituire {}
con ()
o viceversa altrove. Per esempio:
while (x < 10) { x += 1 }
Questa non è una chiamata di metodo, quindi non puoi scriverla in nessun altro modo. Bene, puoi inserire parentesi graffe all'interno della parentesi per il condition
, così come usare parentesi all'interno delle parentesi graffe per il blocco di codice:
while ({x < 10}) { (x += 1) }
Quindi, spero che questo aiuti.