Dans Scala, je peux créer une classe de cas, case class Foo(x:Int)
, puis la mettre dans une liste comme ceci:
List(Foo(42))
Maintenant, rien d'étrange ici. Ce qui suit est étrange pour moi. L'opérateur ::
est une fonction sur une liste, non? Avec n'importe quelle fonction avec un argument dans Scala, je peux l'appeler avec la notation infixe. Un exemple est 1 + 2
est une fonction (+)
sur l'objet Int
. La classe Foo
que je viens de définir n'a pas le ::
, alors comment est-ce possible?
Foo(40) :: List(Foo(2))
Dans Scala 2.8 RC1, j'obtiens la sortie suivante de l'invite interactive:
scala> case class Foo(x:Int)
defined class Foo
scala> Foo(40) :: List(Foo(2))
res2: List[Foo] = List(Foo(40), Foo(2))
Je peux continuer et l'utiliser, mais quelle est l'explication?
De la spécification:
6.12.3 InfixOperations Un opérateur infixe peut être un identifiant arbitraire. Les opérateurs Infix ont la priorité et l'associativité définies comme suit.
...
L'associativité d'un opérateur est déterminée par le dernier caractère de l'opérateur. Les opérateurs se terminant par deux points ":" sont associatifs à droite. Tous les autres opérateurs sont associatifs à gauche.
Vous pouvez toujours voir comment ces règles sont appliquées dans Scala en imprimant le programme après avoir traversé la phase 'typer' du compilateur:
scala -Xprint:typer -e "1 :: Nil"
val r: List[Int] = {
<synthetic> val x$1: Int = 1;
immutable.this.Nil.::[Int](x$1)
};
Il se termine par un :
. Et c'est le signe que cette fonction est définie dans la classe de droite (ici dans la classe List
).
C'est donc List(Foo(2)).::(Foo(40))
, pas Foo(40).::(List(Foo(2)))
dans votre exemple.
Un aspect manquant dans les réponses données est que pour supporter ::
Dans les expressions de correspondance de motifs:
List(1,2) match {
case x :: xs => println(x + " " + xs)
case _ => println("")
}
final case class ::[B](private var hd: B, private[scala] var tl: List[B])
donc case ::(x,xs)
produirait le même résultat. L'expression case x :: xs
Fonctionne car l'extracteur par défaut ::
Est défini pour la classe de cas et il peut être utilisé infix.
La classe
Foo
que je viens de définir n'a pas l'opérateur::
, Alors comment est-ce possible:
Foo(40) :: List(Foo(2))
Si le nom de la méthode se termine par deux points (:
), La méthode est invoquée sur opérande de droite, ce qui est le cas ici. Si le nom de la méthode ne se termine pas par deux points, la méthode est invoquée sur l'opérande de gauche. Par exemple, a + b
, +
Est invoqué sur a
.
Ainsi, dans votre exemple, ::
Est une méthode sur son opérande droit, qui est un List
.