J'ai essayé une fois d'écrire à ce sujet, mais j'ai finalement abandonné, car les règles sont quelque peu diffuses. Fondamentalement, vous devrez vous y familiariser.
Il est peut-être préférable de se concentrer sur les endroits où les accolades et les parenthèses peuvent être utilisées de manière interchangeable: lors du passage des paramètres aux appels de méthode. Vous pouvez remplacer les parenthèses par des accolades si et seulement si la méthode attend un seul paramètre. Par exemple:
List(1, 2, 3).reduceLeft{_ + _} // valid, single Function2[Int,Int] parameter
List{1, 2, 3}.reduceLeft(_ + _) // invalid, A* vararg parameter
Cependant, vous devez en savoir plus pour mieux comprendre ces règles.
Augmentation de la vérification de la compilation avec des parens
Les auteurs de Spray recommandent des parens ronds car ils permettent une vérification accrue de la compilation. Ceci est particulièrement important pour les DSL comme Spray. En utilisant des parens, vous dites au compilateur qu'il ne doit lui être donné qu'une seule ligne; donc si vous lui en donnez accidentellement deux ou plus, il se plaindra. Maintenant, ce n'est pas le cas avec les accolades - si, par exemple, vous oubliez un opérateur quelque part, votre code se compilera et vous obtiendrez des résultats inattendus et potentiellement un bogue très difficile à trouver. Ci-dessous est artificiel (puisque les expressions sont pures et donneront au moins un avertissement), mais fait le point:
method {
1 +
2
3
}
method(
1 +
2
3
)
Le premier compile, le second donne error: ')' expected but integer literal found
. L'auteur a voulu écrire 1 + 2 + 3
.
On pourrait dire que c'est similaire pour les méthodes multi-paramètres avec des arguments par défaut; il est impossible d'oublier accidentellement une virgule pour séparer les paramètres lors de l'utilisation de parens.
Verbosité
Une note importante souvent négligée sur la verbosité. L'utilisation d'accolades conduit inévitablement à un code détaillé car le guide de style Scala indique clairement que la fermeture des accolades doit être sur leur propre ligne:
… L'accolade fermante est sur sa propre ligne immédiatement après la dernière ligne de la fonction.
De nombreux reformatages automatiques, comme dans IntelliJ, effectueront automatiquement ce reformatage pour vous. Essayez donc de vous en tenir à des rondelles rondes lorsque vous le pouvez.
Notation d'infixe
Lorsque vous utilisez la notation infixe, comme List(1,2,3) indexOf (2)
vous pouvez omettre les parenthèses s'il n'y a qu'un seul paramètre et l'écrire comme List(1, 2, 3) indexOf 2
. Ce n'est pas le cas de la notation par points.
Notez également que lorsque vous avez un seul paramètre qui est une expression à plusieurs jetons, comme x + 2
ou a => a % 2 == 0
, vous devez utiliser des parenthèses pour indiquer les limites de l'expression.
Tuples
Parce que vous pouvez parfois omettre des parenthèses, parfois un tuple a besoin de parenthèses supplémentaires comme dans ((1, 2))
, et parfois la parenthèse externe peut être omise, comme dans (1, 2)
. Cela peut être source de confusion.
Littéraux de fonction / fonction partielle avec case
Scala a une syntaxe pour les littéraux de fonction et de fonction partielle. Cela ressemble à ceci:
{
case pattern if guard => statements
case pattern => statements
}
Les seuls autres endroits où vous pouvez utiliser des case
instructions sont les mots clés match
et catch
:
object match {
case pattern if guard => statements
case pattern => statements
}
try {
block
} catch {
case pattern if guard => statements
case pattern => statements
} finally {
block
}
Vous ne pouvez pas utiliser des case
instructions dans un autre contexte . Donc, si vous voulez utiliser case
, vous avez besoin d' appareils orthopédiques. Au cas où vous vous demandez ce qui fait la distinction entre une fonction et une fonction partielle littérale, la réponse est: le contexte. Si Scala attend une fonction, une fonction que vous obtenez. S'il attend une fonction partielle, vous obtenez une fonction partielle. Si les deux sont attendus, cela donne une erreur d'ambiguïté.
Expressions et blocs
Des parenthèses peuvent être utilisées pour créer des sous-expressions. Les accolades peuvent être utilisées pour créer des blocs de code (ce n'est pas une fonction littérale, alors méfiez-vous d'essayer de l'utiliser comme une seule). Un bloc de code se compose de plusieurs instructions, chacune pouvant être une instruction d'importation, une déclaration ou une expression. Ça va comme ça:
{
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 )
Donc, si vous avez besoin de déclarations, de plusieurs déclarations, import
ou quelque chose comme ça, vous avez besoin d'accolades. Et comme une expression est une déclaration, des parenthèses peuvent apparaître entre accolades. Mais la chose intéressante est que les blocs de code sont également des expressions, vous pouvez donc les utiliser n'importe où dans une expression:
( { var x = 0; while (x < 10) { x += 1}; x } % 5) + 1
Donc, comme les expressions sont des instructions et que les blocs de codes sont des expressions, tout ce qui suit est valide:
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...
Où ils ne sont pas interchangeables
Fondamentalement, vous ne pouvez pas remplacer {}
par ()
ou vice versa ailleurs. Par exemple:
while (x < 10) { x += 1 }
Ce n'est pas un appel de méthode, vous ne pouvez donc pas l'écrire autrement. Eh bien, vous pouvez mettre des accolades à l' intérieur des parenthèses pour le condition
, ainsi que des parenthèses à l' intérieur des accolades pour le bloc de code:
while ({x < 10}) { (x += 1) }
J'espère donc que cela aide.
{}
- tout devrait être une seule expression pureList{1, 2, 3}.reduceLeft(_ + _)
invalide, voulez-vous dire qu'il a une erreur de syntaxe? Mais je trouve que le code peut être compilé. Je mets mon code iciList(1, 2, 3)
dans tous les exemples, au lieu deList{1, 2, 3}
. Hélas, sur la version actuelle de Scala (2.13), cela échoue avec un message d'erreur différent (virgule inattendue). Vous devrez probablement revenir à 2.7 ou 2.8 pour obtenir l'erreur d'origine, probablement.Il y a quelques règles et inférences différentes en cours ici: tout d'abord, Scala déduit les accolades lorsqu'un paramètre est une fonction, par exemple dans
list.map(_ * 2)
les accolades sont inférées, c'est juste une forme plus courte delist.map({_ * 2})
. Deuxièmement, Scala vous permet d'ignorer les parenthèses sur la dernière liste de paramètres, si cette liste de paramètres a un paramètre et qu'il s'agit d'une fonction, ellelist.foldLeft(0)(_ + _)
peut donc être écrite commelist.foldLeft(0) { _ + _ }
(oulist.foldLeft(0)({_ + _})
si vous voulez être plus explicite).Cependant, si vous ajoutez,
case
vous obtenez, comme d'autres l'ont mentionné, une fonction partielle au lieu d'une fonction, et Scala ne déduira pas les accolades pour les fonctions partielles, donclist.map(case x => x * 2)
ne fonctionnera pas, mais les deuxlist.map({case x => 2 * 2})
et lelist.map { case x => x * 2 }
fera.la source
list.foldLeft{0}{_+_}
marche.La communauté s'efforce de normaliser l'utilisation des accolades et des parenthèses, voir Guide de style Scala (page 21): http://www.codecommit.com/scala-style-guide.pdf
La syntaxe recommandée pour les appels de méthodes d'ordre supérieur consiste à toujours utiliser des accolades et à ignorer le point:
Pour les appels de métodes "normaux", vous devez utiliser le point et les parenthèses.
la source
+
,--
), PAS les méthodes régulières commetakeWhile
. Le point entier de la notation infixe est d'autoriser les DSL et les opérateurs personnalisés, donc on ne devrait pas l'utiliser dans ce contexte tout le temps.Je ne pense pas qu'il y ait quelque chose de particulier ou de complexe dans les accolades frisées à Scala. Pour maîtriser leur utilisation apparemment complexe dans Scala, gardez simplement à l'esprit quelques points simples:
Expliquons quelques exemples selon les trois règles ci-dessus:
la source
{}
comportement. J'ai mis à jour le libellé pour plus de précision. Et pour 4, c'est un peu délicat en raison de l'interaction entre()
et{}
, en tant quedef f(x: Int): Int = f {x}
travaux, et c'est pourquoi j'ai eu le 5ème. :)fun f(x) = f x
est valide en SML.f {x}
commef({x})
cela semble être une meilleure explication pour moi, car penser()
et{}
interchangeable est moins intuitif. À propos, l'f({x})
interprétation est quelque peu soutenue par la spécification Scala (section 6.6):ArgumentExprs ::= ‘(’ [Exprs] ‘)’ | ‘(’ [Exprs ‘,’] PostfixExpr ‘:’ ‘_’ ‘*’ ’)’ | [nl] BlockExp
Je pense qu'il vaut la peine d'expliquer leur utilisation dans les appels de fonction et pourquoi diverses choses se produisent. Comme quelqu'un l'a déjà dit, les accolades définissent un bloc de code, qui est également une expression, donc peut être placé là où l'expression est attendue et elle sera évaluée. Lorsqu'elles sont évaluées, ses instructions sont exécutées et la valeur de la dernière instruction est le résultat de l'évaluation de bloc entier (un peu comme dans Ruby).
Ayant cela, nous pouvons faire des choses comme:
Le dernier exemple est juste un appel de fonction avec trois paramètres, dont chacun est évalué en premier.
Maintenant, pour voir comment cela fonctionne avec les appels de fonction, définissons une fonction simple qui prend une autre fonction comme paramètre.
Pour l'appeler, nous devons passer une fonction qui prend un paramètre de type Int, afin que nous puissions utiliser la fonction littérale et la passer à foo:
Maintenant, comme dit précédemment, nous pouvons utiliser un bloc de code à la place d'une expression, alors utilisons-le
Ce qui se passe ici, c'est que le code à l'intérieur de {} est évalué et que la valeur de la fonction est renvoyée comme valeur de l'évaluation du bloc, cette valeur est ensuite passée à foo. C'est sémantiquement le même que l'appel précédent.
Mais nous pouvons ajouter quelque chose de plus:
Maintenant, notre bloc de code contient deux instructions, et parce qu'il est évalué avant l'exécution de foo, ce qui se passe est que le premier "Hey" est imprimé, puis notre fonction est passée à foo, "Entering foo" est imprimé et enfin "4" est imprimé .
Cela semble un peu moche cependant et Scala nous permet de sauter la parenthèse dans ce cas, afin que nous puissions écrire:
ou
Cela semble beaucoup plus agréable et équivaut aux précédents. Ici encore, le bloc de code est évalué en premier et le résultat de l'évaluation (qui est x => println (x)) est passé en argument à foo.
la source
foo({ x => println(x) })
. Peut-être que je suis trop coincé dans mes voies ...Parce que vous utilisez
case
, vous définissez une fonction partielle et les fonctions partielles nécessitent des accolades.la source
Augmentation de la vérification de la compilation avec des parens
Les auteurs de Spray recommandent que les parens ronds donnent une vérification accrue de la compilation. Ceci est particulièrement important pour les DSL comme Spray. En utilisant des parens, vous dites au compilateur qu'il ne doit lui être donné qu'une seule ligne, donc si vous en avez accidentellement donné deux ou plus, il se plaindra. Maintenant, ce n'est pas le cas avec les accolades, si par exemple, vous oubliez un opérateur quelque part que votre code compilera, vous obtiendrez des résultats inattendus et potentiellement un bogue très difficile à trouver. Ci-dessous est artificiel (puisque les expressions sont pures et donneront au moins un avertissement), mais fait le point
Le premier compile, le second donne à
error: ')' expected but integer literal found.
l'auteur voulu écrire1 + 2 + 3
.On pourrait dire que c'est similaire pour les méthodes multi-paramètres avec des arguments par défaut; il est impossible d'oublier accidentellement une virgule pour séparer les paramètres lors de l'utilisation de parens.
Verbosité
Une note importante souvent négligée sur la verbosité. L'utilisation d'accolades conduit inévitablement à un code détaillé car le guide de style scala indique clairement que les accolades fermantes doivent être sur leur propre ligne: http://docs.scala-lang.org/style/declarations.html "... l'accolade fermante est sur sa propre ligne immédiatement après la dernière ligne de la fonction. " De nombreux reformatages automatiques, comme dans Intellij, effectueront automatiquement ce reformatage pour vous. Essayez donc de vous en tenir à des rondelles rondes lorsque vous le pouvez. Par exemple
List(1, 2, 3).reduceLeft{_ + _}
devient:la source
Avec des accolades, vous avez un point-virgule induit pour vous et pas des parenthèses. Considérez la
takeWhile
fonction, car elle attend une fonction partielle, seule la{case xxx => ??? }
définition est valide au lieu des parenthèses autour de l'expression de la casse.la source