haskell enumerators conduit

haskell - ¿Cuáles son los pros y contras de Enumerators vs. Conduits vs. Pipes?



(2)

Me gustaría saber de alguien con un conocimiento más profundo que yo mismo cuáles son las diferencias fundamentales entre los Enumerators , Conduits y Pipes , así como los beneficios y desventajas clave. Algunas discusiones ya están en ongoing pero sería bueno tener una visión general de alto nivel.


Después de escribir aplicaciones con las tres bibliotecas, creo que la mayor diferencia que he visto es en cómo se maneja la finalización de recursos. Por ejemplo, Pipes divide la finalización del recurso en tipos separados de marcos y pilas.

También parece todavía haber cierto debate sobre cómo no solo finalizar el recurso de entrada, sino también potencialmente el recurso de salida. Por ejemplo, si está leyendo desde un DB y está escribiendo en un archivo, la conexión para el DB debe cerrarse, así como el archivo de salida que necesita ser enjuagado y cerrado. Las cosas se ponen feas cuando se decide cómo manejar excepciones y casos de fallas a lo largo de la tubería.

Otra diferencia más sutil parece ser cómo se maneja y calcula el valor de retorno de la tubería del enumerador.

Muchas de estas diferencias y posibles incoherencias han quedado al descubierto por el uso de las implementaciones Monad y Category para Pipes y ahora están abriéndose camino en Conduits.


Los enumeradores / Iteratos como abstracción fueron inventados por Oleg Kiselyov. Proporcionan una forma limpia de hacer IO con requisitos de recursos predecibles (bajos). El paquete de Enumerators actual es bastante similar al trabajo original de Oleg.

Los conductos fueron creados para el marco web Yesod. Tengo entendido que fueron diseñados para ser increíblemente rápidos. Las primeras versiones de la biblioteca eran muy explícitas.

Las tuberías se enfocan en la elegancia. Tienen un solo tipo en lugar de varios, forman instancias de mónada (transformador) y categoría, y tienen un diseño muy "funcional".

Si le gustan las explicaciones categóricas: el tipo de Pipe es solo la mónada gratuita sobre el siguiente functor simple impío

data PipeF a b m r = M (m r) | Await (a -> r) | Yield b r instance Monad m => Functor (PipeF a b m) where fmap f (M mr) = M $ liftM mr fmap f (Await g) = Await $ f . g fmap f (Yield b p) = Yield b (f p) --Giving: newtype Pipe a b m r = Pipe {unPipe :: Free (PipeF a b m) r} deriving (Functor, Applicative, Monad) --and instance MonadTrans (Pipe a b) where lift = Pipe . inj . M

En la definición de tubería actual, estos están cocidos, pero la simplicidad de esta definición es sorprendente. Las tuberías forman una categoría debajo de la operación (<+<) :: Monad m => Pipe cdmr -> Pipe abmr -> Pipe admr que toma todo lo que la primera tubería yields y lo alimenta a la segunda tubería en espera.

Parece que Conduits está moviendo para ser más similar a un Pipe (usando CPS en lugar de estado y cambiando a un solo tipo) mientras que las tuberías están ganando soporte para un mejor manejo de errores, y quizás el retorno de tipos separados para generadores y consumidores.

Esta área se está moviendo rápidamente. He estado pirateando una variante experimental de la biblioteca Pipe con estas características, y sé que otras personas también (ver el paquete Guarded Pipes en Hackage), pero sospecho que Gabriel (el autor de Pipes) las resolverá antes que yo. hacer.

Mis recomendaciones: si usa Yesod, use Conduits. Si quieres una biblioteca madura usa Enumerator. Si te preocupas por la elegancia, utiliza Pipe.