Estos se denominan restricciones de tipo generalizadas . Le permiten, desde dentro de una clase o rasgo con parámetros de tipo, restringir aún más uno de sus parámetros de tipo. Aquí hay un ejemplo:
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
}
El evidencecompilador proporciona el argumento implícito , iff Aes String. Se puede pensar en él como una prueba de que Aes String--el argumento en sí mismo no es importante, sólo el saber que existe. [edit: bueno, técnicamente en realidad es importante porque representa una conversión implícita de Aa String, que es lo que te permite llamar a.lengthy no que el compilador te grite]
Ahora puedo usarlo así:
scala> Foo("blah").getStringLength
res6: Int = 4
Pero si intenté usarlo con un Foocontenido que no sea un String:
scala> Foo(123).getStringLength
<console>:9: error: could not find implicit value for parameter evidence: =:=[Int,String]
Puede leer ese error como "no se pudo encontrar evidencia de que Int == String" ... ¡así es como debería ser! getStringLengthestá imponiendo restricciones en el tipo de Alo que Fooen general requiere; a saber, solo puede invocar getStringLengthen un Foo[String]. Esta restricción se aplica en tiempo de compilación, ¡lo cual es genial!
<:<y <%<funcionan de manera similar, pero con ligeras variaciones:
A =:= B significa que A debe ser exactamente B
A <:< Bsignifica que A debe ser un subtipo de B (análogo a la restricción de tipo simple<: )
A <%< Bsignifica que A debe ser visible como B, posiblemente mediante conversión implícita (análoga a la restricción de tipo simple <%)
Este fragmento de @retronym es una buena explicación de cómo se lograba este tipo de cosas y cómo las restricciones de tipo generalizadas lo hacen más fácil ahora.
APÉNDICE
Para responder a su pregunta de seguimiento, es cierto que el ejemplo que di es bastante artificial y obviamente no es útil. Pero imagine usarlo para definir algo así como un List.sumIntsmétodo, que suma una lista de enteros. No desea permitir que este método se invoque en ningún antiguo List, solo a List[Int]. Sin embargo, el Listconstructor de tipos no puede ser tan limitado; aún desea poder tener listas de cadenas, foos, barras y demás. Por lo tanto, al colocar una restricción de tipo generalizada sumInts, puede asegurarse de que solo ese método tenga una restricción adicional que solo se pueda usar en un List[Int]. Esencialmente, está escribiendo un código de caso especial para ciertos tipos de listas.