¿Por qué necesita clases más altas?

13

Algunos idiomas permiten clases y funciones con parámetros de tipo (como List<T> donde T puede ser un tipo arbitrario). Por ejemplo, puede tener una función como:

List<S> Function<S, T>(List<T> list)

Sin embargo, algunos idiomas permiten que este concepto se extienda un nivel más alto, lo que le permite tener una función con la firma:

K<S> Function<K<_>, S, T>(K<T> arg)

Donde K<_> en sí es un tipo como List<_> que tiene un parámetro de tipo. Este "tipo parcial" se conoce como un constructor de tipo.

Mi pregunta es, ¿por qué necesitas esta habilidad? Tiene sentido tener un tipo como List<T> porque todos los List<T> son casi exactamente iguales, pero todos los K<_> pueden ser completamente diferentes. Puede tener un Option<_> y un List<_> que no tienen ninguna funcionalidad común.

    
pregunta GregRos 17.03.2015 - 18:05

1 respuesta

4

Como nadie más ha respondido a la pregunta, creo que lo intentaré yo mismo. Voy a tener que ponerme un poco filosófico.

La programación genérica consiste en abstraer sobre tipos similares, sin la pérdida de información de tipo (que es lo que sucede con el polimorfismo de valores orientados a objetos). Para hacer esto, los tipos necesariamente deben compartir algún tipo de interfaz (un conjunto de operaciones, no el término OO) que puede usar.

En los lenguajes orientados a objetos, los tipos satisfacen una interfaz en virtud de las clases. Cada clase tiene su propia interfaz, definida como parte de su tipo. Dado que todas las clases List<T> comparten la misma interfaz, puede escribir código que funcione sin importar qué T elija. Otra forma de imponer una interfaz es una restricción de herencia, y aunque las dos parecen diferentes, son similares si lo piensas.

En la mayoría de los lenguajes orientados a objetos, List<> no es un tipo adecuado en sí mismo. No tiene métodos, y por lo tanto no tiene interfaz. Es solo List<T> que tiene estas cosas. Esencialmente, en términos más técnicos, los únicos tipos que se pueden abstraer significativamente son aquellos con el tipo * . Para hacer uso de los tipos de tipo superior en un mundo orientado a objetos, tiene que expresar las restricciones de tipo de manera consistente con esta restricción.

Por ejemplo, como se menciona en los comentarios, podemos ver a Option<> y List<> como "asignables", en el sentido de que si tiene una función, puede convertir un Option<T> en un Option<S> , o un List<T> en un List<S> . Recordando que las clases no se pueden usar para abstraerse directamente sobre tipos de tipo superior, en su lugar hacemos una interfaz:

IMappable<K<_>, T> where K<T> : IMappable<K<_>, T>

Y luego implementamos la interfaz en List<T> y Option<T> como IMappable<List<_>, T> y IMappable<Option<_>, T> respectivamente. Lo que hemos hecho es usar tipos de tipo más alto para colocar restricciones en los tipos reales (no de tipo más alto) Option<T> y List<T> . Así es como se hace en Scala, aunque, por supuesto, Scala tiene características como rasgos, variables de tipo y parámetros implícitos que lo hacen más expresivo.

En otros idiomas, es posible abstraer directamente sobre tipos de tipo superior. En Haskell, una de las más altas autoridades en sistemas de tipo, podemos formular una clase de tipo para cualquier tipo, incluso si tiene un tipo más alto. Por ejemplo,

class Mappable mp where
    map :: mp a -> mp b

Esta es una restricción colocada directamente en un tipo (no especificado) mp que toma un parámetro de tipo, y requiere que se asocie con la función map que convierte un mp<a> en un mp<b> . Luego, podemos escribir funciones que restringen los tipos de tipo superior por Mappable , al igual que en los lenguajes orientados a objetos, podría colocar una restricción de herencia. Bueno, más o menos.

Para resumir, su capacidad para utilizar tipos de tipo superior depende de su capacidad para restringirlos o usarlos como parte de las restricciones de tipo.

    
respondido por el GregRos 20.03.2015 - 12:57

Lea otras preguntas en las etiquetas