El paquete ListLike parece proporcionar lo que estás buscando. Nunca entendí por qué no es más popular.
ListLike aparte, una razón por la que esto no está implementado en el Preludio es porque no es posible hacerlo bien sin invocar algunas extensiones de lenguaje (clases de tipo multi-param y fundeps o tipos asociados). Hay tres tipos de recipientes a considerar:
- Los envases que no se preocupan por sus elementos en todo (por ejemplo, [])
- contenedores que sólo se implementan para elementos específicos (por ejemplo, cadenas de bytes)
- Contenedores que son elementos polimórficos sobre pero requieren un contexto (por ejemplo, Data.Vector.Storable, que contendrá cualquier tipo con una instancia almacenable).
Aquí es una clase muy básica de estilo ListLike sin necesidad de utilizar ninguna extensión:
class Listable container where
head :: container a -> a
instance Listable [] where
head (x:xs) = x
instance Listable ByteString where --compiler error, wrong kind
instance Listable SV.Vector where
head v = SV.head --compiler error, can't deduce context (Storable a)
Aquí tiene container
tipo *->*
. Esto no funcionará para las cadenas de bytes porque no permiten un tipo arbitrario; tienen el tipo *
. Tampoco funcionará para un vector Data.Vector.Storable, porque la clase no incluye el contexto (la restricción Storable).
Puede solucionar este problema, ya sea cambiando su definición de clase a
class ListableMPTC container elem | container -> elem where
o
class ListableAT container where
type Elem container :: *
Ahora tiene container
tipo *
; es un constructor de tipos totalmente aplicado. Es decir, sus instancias se ven como
instance ListableMPTC [a] a where
pero ya no es Haskell98.
Es por eso que incluso una simple interfaz de tipo Listable no es trivial; se vuelve un poco más difícil cuando se tiene en cuenta una semántica de colección diferente (por ejemplo, colas). El otro gran desafío son los datos mutables frente a los inmutables. Hasta el momento, cada intento que he visto (excepto uno) se basa en ese problema al crear una interfaz mutable e inmutable. La única interfaz que conozco que unificó los dos fue alucinante, invocó un montón de extensiones y tuvo un rendimiento bastante pobre.
Adición: cadenas de bytes
totalmente conjetura de mi parte, pero creo que estamos atascados con cadenas de bytes como un producto de la evolución. Es decir, fueron la primera solución para las operaciones de E/S de bajo rendimiento, y tenía sentido usar Ptr Word8
para interactuar con las llamadas al sistema IO. Las operaciones en punteros requieren Storable, y lo más probable es que las extensiones necesarias (como se describió anteriormente) para hacer que el polimorfismo no esté disponible. Ahora es difícil superar su impulso. Un contenedor similar con polimorfismo es ciertamente posible, el paquete storablevector implementa esto, pero no es ni de lejos tan popular.
¿Pueden las cadenas de bytes ser polimórficas sin ninguna restricción en los elementos? Creo que el Haskell más cercano tiene este es el tipo de matriz. Esto no es tan bueno como una cadena de bytes para IO de bajo nivel porque los datos deben desempaquetarse desde el puntero al formato interno de la matriz. Además, los datos están encuadrados, lo que agrega una sobrecarga de espacio significativa. Si desea un almacenamiento sin caja (menos espacio) y una interfaz eficiente con C, los indicadores son el camino a seguir. Una vez que tiene un Ptr, necesita Storable, y luego necesita incluir el tipo de elemento en la clase de tipo, por lo que le quedan extensiones que requieren.
Dicho esto, creo que con las extensiones adecuadas disponibles esto es esencialmente un problema resuelto para cualquier implementación de contenedor único (módulo mutable/inmutable API). La parte más difícil ahora es crear un conjunto sensato de clases que se puedan usar para muchos tipos diferentes de estructuras (listas, matrices, colas, etc.) y que sea lo suficientemente flexible como para ser útil. Personalmente, espero que esto sea relativamente sencillo, pero podría estar equivocado.
Puede explicar cómo se imagina este trabajo? Claramente no es posible tener una clase de tipo con 'ByteString' y' [] 'como instancias, ya que' [] 'tiene kind' * -> * 'y' ByteString' es solo '*'. –
@Travis Brown: puede hacerlo con un contenedor de tipo nuevo trivialmente parametrizado. Esto se ha reinventado varias veces, pero un ejemplo está aquí http://hackage.haskell.org/packages/archive/iteratee/0.2.1/doc/html/Data-Iteratee-WrappedByteString.html – Anthony
Si hay una biblioteca que lo hace lo que quiere, ¿por qué debería incluirse en el idioma propiamente dicho? –