Un amigo mío planteó una pregunta aparentemente inofensiva sobre el lenguaje Scala la semana pasada que no tuve una buena respuesta a: si hay una forma fácil de declarar una colección de elementos pertenecientes a alguna clase de tipo común . Por supuesto, no existe una noción de primera clase de "tipo de letra" en Scala, por lo que tenemos que pensar en esto en términos de rasgos y límites de contexto (es decir, implícitas).Tipos imprsicativos vs. subtipificación antigua simple
Concretamente, dado algún rasgo T[_]
que representa una clase de tipos y tipos A
, B
y C
, la implícitos correspondientes en su alcance T[A]
, T[B]
y T[C]
, queremos declarar algo así como un List[T[a] forAll { type a }]
, en la que podemos lanzar instancias de A
, B
y C
con impunidad. Esto, por supuesto, no existe en Scala; a question last year analiza esto con más profundidad.
La siguiente pregunta natural es "¿Cómo lo hace Haskell?" Bueno, GHC en particular tiene una extensión de sistema tipo llamada impredicative polymorphism, descrita en el documento "Boxy Types". En resumen, dada una clase de tipos T
, uno puede construir legalmente una lista [forall a. T a => a]
. Dada una declaración de este formulario, el compilador hace algo de magia de paso de diccionario que nos permite retener las instancias de clase de tipo correspondientes a los tipos de cada valor en la lista en tiempo de ejecución.
La cosa es que "magia que pasa el diccionario" se parece mucho a "vtables". En un lenguaje orientado a objetos como Scala, la subtipificación es un mecanismo mucho más simple y natural que el enfoque de "Tipos de Boxy". Si nuestro A
, B
y C
extienden el rasgo T
, entonces simplemente podemos declarar List[T]
y ser felices. Del mismo modo, como señala Miles en un comentario a continuación, si todos amplían los rasgos T1
, T2
y T3
, entonces puedo usar List[T1 with T2 with T3]
como equivalente al imprevisible Haskell [forall a. (T1 a, T2 a, T3 a) => a]
.
Sin embargo, la principal desventaja, conocido con subtipos en comparación con las clases de tipos es estrecho acoplamiento: mis A
, B
y C
tipos tienen que tener su comportamiento T
cuece en Asumamos que este es un motivo de ruptura importante, y yo puede. 't use subtipificación. Por lo que el término medio en Scala es proxenetas^conversiones H^H^H^H^Himplicit: dado algunos A => T
, B => T
y C => T
alcance implícita, lo que puedo de nuevo felizmente llenar un List[T]
con mis A
, B
y valores C
...
... Hasta que queramos List[T1 with T2 with T3]
. En ese momento, incluso si tenemos conversiones implícitas A => T1
, A => T2
y A => T3
, no podemos poner un A
en la lista. Podríamos reestructurar nuestras conversiones implícitas para proporcionar literalmente A => T1 with T2 with T3
, pero nunca antes había visto a nadie hacer eso, y parece ser otra forma de acoplamiento ajustado.
bien, así que mi pregunta es, finalmente, supongo, una combinación de un par de preguntas que antes se le preguntó aquí: "why avoid subtyping?" y "advantages of subtyping over typeclasses" ... ¿hay alguna teoría unificadora que dice que el polimorfismo impredicativo y el polimorfismo subtipo son una y la misma ? ¿Son las conversiones implícitas de alguna manera el amor secreto de los dos? ¿Y puede alguien articular un patrón bueno y limpio para expresar límites múltiples (como en el último ejemplo anterior) en Scala?
Esto podría ser relevante: http://stackoverflow.com/questions/7213676/forall-in-scala – missingfaktor
@missingfaktor ciertamente lo es, ¡por eso lo vinculé! – mergeconflict
Aah, lo siento, lo perdí en la primera lectura! – missingfaktor