Ceux-ci sont appelés généralisée type de contraintes. Ils vous permettent, à partir de l'intérieur d'un type paramétré de classe ou de trait, à restreignent encore davantage l' un de ses paramètres de type. Voici un exemple:
case class Foo[A](a:A) { // 'A' can be substituted with any type
// getStringLength can only be used if this is a Foo[String]
def getStringLength(implicit evidence: A =:= String) = a.length
}
L'argument implicite evidence
est fourni par le compilateur, iff A
est String
. Vous pouvez la considérer comme une preuve qu' A
est String
--l'argument lui-même n'est pas important, seule en sachant qu'il en existe. [edit: eh bien, techniquement, il est réellement important, car il représente une conversion implicite de A
de String
, ce qui est ce que vous permet de téléphoner a.length
et ne pas avoir le compilateur hurler à vous]
Maintenant je peux l'utiliser comme ceci:
scala> Foo("blah").getStringLength
res6: Int = 4
Mais si j'ai essayé de l'utiliser avec un Foo
contenant autre chose qu'un String
:
scala> Foo(123).getStringLength
<console>:9: error: could not find implicit value for parameter evidence: =:=[Int,String]
Vous pouvez y lire que le message d'erreur "ne pouvait pas trouver des preuves d'Int == Chaîne de caractères"... c'est comme il se doit! getStringLength
est d'imposer de nouvelles restrictions sur le type d' A
de ce qu' Foo
en général l'exige; à savoir, vous ne pouvez invoquer getStringLength
sur Foo[String]
. Cette contrainte est appliquée au moment de la compilation, ce qui est cool!
<:<
et <%<
fonctionnent de la même façon, mais avec de légères variations:
-
A =:= B
signifie Un doit être exactement de B
-
A <:< B
signifie Un doit être un sous-type de B (analogue à la simple contrainte de type <:
)
-
A <%< B
signifie Un doit être visible en tant que B, éventuellement par l'intermédiaire de la conversion implicite (analogue à la simple contrainte de type <%
)
Cet extrait par @retronym est une bonne explication de la façon dont ce genre de chose qui sert à faire et comment généralisée des contraintes de type de le rendre plus facile maintenant.
ADDENDUM
Pour répondre à votre question, il est vrai que l'exemple que j'ai donné est assez artificiel et pas forcément utile. Mais imaginez l'utiliser pour définir quelque chose comme un List.sumInts
méthode, qui s'ajoute à une liste d'entiers. Vous ne souhaitez pas autoriser cette méthode pour être appelé sur un vieux - List
, juste un List[Int]
. Cependant, l' List
type constructeur ne peut pas être contraintes; vous voulez toujours être en mesure d'avoir des listes de chaînes, foos, des bars, et dressoirs. Ainsi en plaçant une généralisé type de contrainte sur sumInts
, vous pouvez vous assurer que juste que la méthode a une contrainte supplémentaire qu'il peut seulement être utilisé sur un List[Int]
. Essentiellement, vous êtes en train de rédiger spéciaux-code cas pour certains types de listes.