scala - para - hashtag seguidores
¿Qué es y cuándo usar la palabra clave forSome de Scala? (2)
¿Cuál es la diferencia entre la List[T] forSome {type T}
y la List[T forSome {type T}]
? ¿Cómo los leo en "inglés"? ¿Cómo debo asimilar la palabra clave forSome
? ¿Cuáles son algunos usos prácticos de forSome
? ¿Cuáles son algunos usos prácticos y más complejos que la simple T forSome {type T}
?
Aquí hay muchas preguntas, y la mayoría de ellas se han abordado de manera bastante exhaustiva en las respuestas vinculadas en los comentarios anteriores, así que responderé a su primera pregunta más concreta.
No hay una diferencia significativa real entre la List[T] forSome { type T }
y la List[T forSome { type T }]
, pero podemos ver una diferencia entre los siguientes dos tipos:
class Foo[A]
type Outer = List[Foo[T]] forSome { type T }
type Inner = List[Foo[T] forSome { type T }]
Podemos leer el primero como "una lista de foos de T
, para algún tipo T
". Hay una sola T
para toda la lista. El segundo, por otro lado, se puede leer como "una lista de foos, donde cada foo es de T
para algo de T
".
Para decirlo de otra manera, si tenemos una lista outer: Outer
, podemos decir que "existe algún tipo T
tal que outer
es una lista de foos de T
", donde para una lista de tipo Inner
, solo podemos diga que "para cada elemento de la lista, existe una T
tal que ese elemento es un foo de T
". Este último es más débil, nos dice menos acerca de la lista.
Entonces, por ejemplo, si tenemos las siguientes dos listas:
val inner: Inner = List(new Foo[Char], new Foo[Int])
val outer: Outer = List(new Foo[Char], new Foo[Int])
El primero compilará bien, cada elemento de la lista es un Foo[T]
para algunos T
El segundo no se compilará, ya que no hay una T
tal que cada elemento de la lista sea un Foo[T]
.
Atención: ( Actualización 2016-12-08 ) Es muy probable que la palabra clave forSome desaparezca con Scala 2.13 o 2.14, de acuerdo con la charla de Martin Odersky sobre ScalaX 2016. Reemplácela con tipos dependientes de la ruta o con atributos de tipo anónimo ( A[_]
). Esto es posible en la mayoría de los casos. Si tiene un caso límite en el que no es posible, refactorice su código o afloje sus restricciones de tipo.
Cómo leer "forSome" (de manera informal)
Por lo general, cuando utiliza una API genérica, la API le garantiza que funcionará con cualquier tipo que proporcione (hasta algunas restricciones dadas). Por lo tanto, cuando usa la List[T]
, la API de la Lista le garantiza que funcionará con cualquier tipo de T
que proporcione.
Con forSome
(los llamados parámetros de tipo cuantificados existencialmente) es al revés. La API proporcionará un tipo (no usted) y le garantiza que funcionará con el tipo que le proporcionó. La semántica es que un objeto concreto le dará algo de tipo T
El mismo objeto también aceptará las cosas que te proporcionó. Pero ningún otro objeto puede funcionar con estas T
s y ningún otro objeto puede proporcionarle algo de tipo T
La idea de "cuantificado existencialmente" es: Existe (al menos) un tipo T
(en la implementación) para cumplir el contrato de la API. Pero no te diré de qué tipo es.
forSome
puede leerse de forma similar: para algunos tipos T
el contrato API es válido. Pero no es necesario cierto para todos los tipos T
Entonces, cuando proporciona algún tipo T
(en lugar del que está oculto en la implementación de la API), el compilador no puede garantizar que obtuvo la T
correcta. Así que tirará un error de tipo.
Aplicado a tu ejemplo.
Entonces, cuando vea la List[T] forSome {type T}
en una API, puede leerlo así: La API le proporcionará una List
de algún tipo desconocido T
Con mucho gusto aceptará esta lista y funcionará con ella. Pero no te dirá, qué es T
Pero sabes, al menos, que todos los elementos de la lista son del mismo tipo T
El segundo es un poco más complicado. Una vez más, la API le proporcionará una List
. Y utilizará algún tipo T
y no te dirá qué es T
Pero es gratis elegir un tipo diferente para cada elemento. Una API del mundo real establecería algunas restricciones para T
, por lo que realmente puede funcionar con los elementos de la lista.
Conclusión
forSome
es útil cuando escribe una API, donde cada objeto representa una implementación de la API. Cada implementación le proporcionará algunos objetos y los volverá a aceptar. Pero no puede mezclar objetos de diferentes implementaciones ni puede crear los objetos usted mismo. En su lugar, siempre debe usar las funciones API correspondientes para obtener algunos objetos que funcionarán con esa API. forSome
permite un tipo muy estricto de encapsulación. Puedes leer para forSome
de la siguiente manera:
El contrato API se pliega como cierto para algunos tipos. Pero no sabes para qué tipos es verdad. Por lo tanto, no puede proporcionar su propio tipo y no puede crear sus propios objetos.
forSome
utilizar los que se proporcionan a través de la API que usaforSome
.
Esto es bastante informal e incluso podría estar equivocado en algunos casos de esquina. Pero debería ayudarte a asimilar el concepto.