web-dev-qa-db-fra.com

L'opérateur '::' de Scala, comment ça marche?

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?

54
Felix

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)
};
48
retronym

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.

21
folone

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("")
}

ne classe :: est définie :

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.

17
Thomas Jung

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.

15
Surya Suravarapu