2012-08-07 21 views
21

He usado zipSinks :: Monad m => Sink i m r -> Sink i m r' -> Sink i m (r, r') para esto, pero se considera obsoleto.¿Cuál es la forma preferida de combinar dos sumideros?

+0

¿Qué comportamiento, * exactamente *, quieres que tengan los fregaderos "combinados"? Traté de ver la documentación anterior y la implementación de 'zipSinks', pero el comportamiento no es fácilmente discernible de un vistazo. –

+0

@DanBurton: 'zipSinks' toma dos Sinks y devuelve un Sink que produce un par con los resultados de los correspondientes Sinks. Por ejemplo, 'sizeCrc32Sink = zipSinks sizeSink crc32Sink' contará el tamaño y la suma de comprobación. Yo qué tipo de comportamiento describe Oleg [aquí] (http://okmij.org/ftp/Streams.html#1enum2iter). – tymmym

+0

Ok, ya veo; básicamente enlaza la espera y alimenta la salida ascendente a ambos sumideros simultáneamente, clasificando la secuencia de entrada en dos. Los documentos para Data.Conduit.Util afirman que "ahora hay formas más fáciles de manejar sus casos de uso", pero no veo una manera más fácil para este caso de uso, ya que requiere profundizar en el interior del conducto para implementarlo. –

Respuesta

6

((El paquete es conduit-0.5.2.3. El conjunto module es sólo para la compatibilidad hacia atrás.))


[edición]

Por lo tanto, mi conjetura monádica sencilla (ver más abajo) parece ser incorrecto, aunque los tipos son correctos. Ahora, solo puedo adivinar que la respuesta es:

Las características de reemplazo están todavía en desarrollo, más o menos como todas las Pipe/Conduit y conceptos y bibliotecas similares.

Esperaría a la próxima API para resolver esta pregunta y aún usaría zipSink hasta entonces. (quizás fue simplemente fuera de lugar.)

[/editar]

No soy tan familiarizados con este paquete, pero ¿no hacer lo mismo que esto?

zipSinks :: Monad m => Sink i m r -> Sink i m r' -> Sink i m (r, r') 
zipSinks s1 s2 = (,) <$> s1 <*> s2 

Es un Monad después de todo. (Functor, Applicative)

zipSinks :: Monad sink => sink r -> sink r' -> sink (r, r') 
zipSinks s1 s2 = liftM2 (,) s1 s2 
+2

Los tipos son correctos, pero no son semánticos. Su versión de 'zipSinks' ejecutará sumideros consecutivamente y el primer receptor consumirá completamente la fuente. – tymmym

7

Editar

Después de considerar esto, no creo que es posible con la versión actual de Data.Conduit. Las tuberías no son categorías, por lo que &&& está fuera de la cuestión. Y no hay forma de que se me ocurra extraer los resultados de la corriente ascendente, alimentarlos gradualmente a ambos sumideros y cortocircuitar cuando termina el primer sumidero. (Aunque no creo que Data.Conduit.Util.zipSinks cortocircuite de esta manera, parece que sería muy deseable.) Excepto por supuesto, para coincidir con el patrón en ambos sumideros (como zipSinks en el paquete), pero eso es lo que estamos tratando de evitar aquí.

Dicho esto, me gustaría amor que se demuestre que está mal aquí.


No es bonito, pero puede hacerlo de una forma obvia.

primeras importaciones:

module Main where 

import Control.Monad.Trans 
import Data.Conduit 
import qualified Data.Conduit.Binary as CB 
import qualified Data.Conduit.List as CL 
import qualified Data.Conduit.Text as CT 
import qualified Data.Conduit.Util as CU 
import Data.Maybe 
import Data.Text (unpack) 

ya por zipSinks. Básicamente, desea crear un receptor que extraiga la entrada desde la parte superior y la envíe a cada receptor secundario por separado. En este caso, he usado CL.sourceList para hacer esto. Si await devuelve Nothing, maybeToList devuelve una lista vacía, por lo que los contenedores secundarios también se ejecutan sin entrada. Finalmente, la salida de cada sumidero de niños se alimenta a la tupla.

zipSinks :: Monad m => Sink i m r -> Sink i m r' -> Sink i m (r, r') 
zipSinks s1 s2 = do 
    l <- fmap maybeToList await 
    o1 <- lift $ CL.sourceList l $$ s1 
    o2 <- lift $ CL.sourceList l $$ s2 
    return (o1, o2) 

Aquí hay algunos ejemplos del uso de zipSinks. Parece funcionar bien tanto dentro de IO como fuera de él, y en las pocas pruebas que hice, la salida coincide con la salida de zipped', creada usando el antiguo zipSinks.

doubleHead :: Monad m => Sink Int m (Maybe Int) 
doubleHead = await >>= return . fmap (2*) 

-- old version 
zipped' :: Monad m => Sink Int m (Maybe Int, Maybe Int) 
zipped' = CU.zipSinks CL.head doubleHead 

-- new version 
zipped :: Monad m => Sink Int m (Maybe Int, Maybe Int) 
zipped = zipSinks CL.head doubleHead 

fromList = CL.sourceList [7, 8, 9] $$ zipped 
-- (Just 7, Just 14) 

fromFile :: String -> IO (Maybe Int, Maybe Int) 
fromFile filename = runResourceT $ 
     CB.sourceFile filename 
    $= CB.lines 
    $= CT.decode CT.utf8 
    $= CL.map (read . unpack) 
    $$ zipped 

-- for a file with the lines: 
-- 
-- 1 
-- 2 
-- 3 
-- 
-- returns (Just 1, Just 2) 
+0

¡Agradable! (NB, podría escribir 'await >> = return. Fmap (2 *)' para 'doubleHead', y de manera similar,' l <- fmap maybeToList await' en lugar de usar 'input' en' zipSinks'. Además, es ' Data.Conduit.Internals' ¿una importación extraña?) – huon

+0

Sí, me di cuenta de que probablemente podría haber usado funtores en algunos lugares. Todavía soy lo suficientemente n00b en Haskell, que por lo general es una edición de segundo pase para mí, desafortunadamente. Y sí, 'Data.Conduits.Internals' es extraño. Originalmente, estaba buscando usar 'sinkToPipe' a partir de él. Gracias por señalar esto. Actualizaré la respuesta. – Eric

+2

Su versión de 'zipSinks' combina solo los primeros elementos. Por ejemplo 'runResourceT $ CL.sourceList [1,2,3] $$ zipSinks (CL.take 2) (CL.take 2)' devolverá '([1], [1])' pero debería '([1 , 2], [1,2]) '. – tymmym

Cuestiones relacionadas