Tentei escrever uma vez sobre isso, mas desisti no final, pois as regras são um pouco difusas. Basicamente, você terá que pegar o jeito.
Talvez seja melhor se concentrar em onde chaves e parênteses podem ser usados de forma intercambiável: ao passar parâmetros para chamadas de método. Você pode substituir parênteses por chaves se, e somente se, o método esperar um único parâmetro. Por exemplo:
List(1, 2, 3).reduceLeft{_ + _} // valid, single Function2[Int,Int] parameter
List{1, 2, 3}.reduceLeft(_ + _) // invalid, A* vararg parameter
No entanto, é preciso saber mais para entender melhor essas regras.
Maior verificação de compilação com parens
Os autores do Spray recomendam parênteses redondos porque fornecem maior verificação de compilação. Isso é especialmente importante para DSLs como o Spray. Ao usar parens, você está dizendo ao compilador que ele deve receber apenas uma única linha; portanto, se você acidentalmente der dois ou mais, ele reclamará. Agora, esse não é o caso das chaves - se, por exemplo, você esquecer um operador em algum lugar, seu código será compilado e você obterá resultados inesperados e, potencialmente, um bug muito difícil de encontrar. Abaixo é inventado (já que as expressões são puras e pelo menos darão um aviso), mas defende o argumento:
method {
1 +
2
3
}
method(
1 +
2
3
)
A primeira compila, a segunda dá error: ')' expected but integer literal found
. O autor quis escrever 1 + 2 + 3
.
Pode-se argumentar que é semelhante para métodos multiparâmetros com argumentos padrão; é impossível esquecer acidentalmente uma vírgula para separar parâmetros ao usar parênteses.
Verbosidade
Uma observação importante, muitas vezes esquecida, sobre a verbosidade. O uso de chavetas inevitavelmente leva a códigos detalhados, pois o guia de estilo Scala afirma claramente que os chavetas de fechamento devem estar em sua própria linha:
… A chave de fechamento está em sua própria linha imediatamente após a última linha da função.
Muitos reformadores automáticos, como no IntelliJ, executam automaticamente essa reformatação para você. Portanto, tente continuar usando parênteses redondas quando puder.
Notação Infix
Ao usar a notação infix, List(1,2,3) indexOf (2)
é possível omitir parênteses se houver apenas um parâmetro e escrevê-lo como List(1, 2, 3) indexOf 2
. Este não é o caso da notação de ponto.
Observe também que, quando você tem um único parâmetro que é uma expressão com vários tokens, como x + 2
ou a => a % 2 == 0
, é necessário usar parênteses para indicar os limites da expressão.
Tuplas
Como você pode omitir parênteses algumas vezes, algumas vezes uma tupla precisa de parênteses extras, como em ((1, 2))
, e algumas vezes o parêntese externo pode ser omitido, como em (1, 2)
. Isso pode causar confusão.
Literais de Função / Função Parcial com case
Scala tem uma sintaxe para literais de função e função parcial. Se parece com isso:
{
case pattern if guard => statements
case pattern => statements
}
Os únicos outros lugares onde você pode usar case
instruções são com as palavras-chave 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
}
Você não pode usar case
instruções em nenhum outro contexto . Então, se você quiser usar case
, precisará de chaves. Caso você esteja se perguntando o que torna literal a distinção entre uma função e uma função parcial, a resposta é: contexto. Se Scala espera uma função, você recebe uma função. Se ele espera uma função parcial, você obtém uma função parcial. Se ambos forem esperados, ocorrerá um erro sobre ambiguidade.
Expressões e blocos
Parênteses podem ser usados para fazer subexpressões. Os chavetas podem ser usadas para criar blocos de código (essa não é uma função literal, portanto, tente usá-la como uma). Um bloco de código consiste em várias instruções, cada uma das quais pode ser uma declaração de importação, uma declaração ou uma expressão. É assim:
{
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 )
Portanto, se você precisar de declarações, várias instruções import
ou algo parecido, precisará de chaves. E como uma expressão é uma declaração, parênteses podem aparecer dentro de chaves. Mas o interessante é que os blocos de código também são expressões, para que você possa usá-los em qualquer lugar dentro de uma expressão:
( { var x = 0; while (x < 10) { x += 1}; x } % 5) + 1
Portanto, como expressões são instruções e blocos de códigos são expressões, tudo abaixo é válido:
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...
Onde eles não são intercambiáveis
Basicamente, você não pode substituir {}
com ()
ou vice-versa em qualquer outro lugar. Por exemplo:
while (x < 10) { x += 1 }
Como não é uma chamada de método, não é possível escrevê-la de nenhuma outra maneira. Bem, você pode colocar chaves dentro dos parênteses para o condition
, bem como usar parênteses dentro dos chaves para o bloco de código:
while ({x < 10}) { (x += 1) }
Então, espero que isso ajude.