2012-08-07 5 views
21

Я использовал zipSinks :: Monad m => Sink i m r -> Sink i m r' -> Sink i m (r, r') для этого, но считается устаревшим.Каков предпочтительный способ объединения двух раковин?

+0

Какое поведение, * точно *, вы хотите иметь «комбинированные» раковины? Я попытался взглянуть на старую документацию и реализацию 'zipSinks', но поведение было легко различимо с первого взгляда. –

+0

@DanBurton: 'zipSinks' берет два раковины и возвращает раковину, которая создает пару с результатами соответствующих Sinks. Например, 'sizeCrc32Sink = zipSinks sizeSink crc32Sink' будет считать размер и контрольную сумму. Я то же самое, что описано Олегом [здесь] (http://okmij.org/ftp/Streams.html#1enum2iter). – tymmym

+0

Хорошо, я вижу; он в основном подключает ожидание и подает выходной сигнал вверх по потоку одновременно на оба приемника, что приводит к разнесению входного потока в два. В документах для Data.Conduit.Util говорится, что «теперь есть более простые способы обработки их случаев использования», но я не вижу более простого способа для этого варианта использования, поскольку для этого требуется вникание в внутренние оболочки кабелепровода. –

ответ

6

((Пакет conduit-0.5.2.3. Вся module только для обратной совместимости.))


[редактировать]

Итак, мой прямой монадическая догадка (см ниже) кажется неправильным, хотя типы правильные. Теперь я могу только догадаться, что ответ такой:

Заменяющие функции все еще находятся в разработке, почти как все Pipe/Conduit и подобные концепции и библиотеки.

Я бы подождал следующего API, чтобы решить этот вопрос и до сих пор использовать zipSink. (Может быть, это был просто неуместны.)

[/редактировать]

Я не то, что знаком с этим пакетом, но не это делать так же, как это?

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

В конце концов, это Monad. (Functor, Applicative)

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

Типы - это нормально, но не семантика. Ваша версия 'zipSinks' будет последовательно запускать раковины, а первая раковина будет полностью потреблять источник. – tymmym

7

Редактировать

После рассмотрения этого я не думаю, что это возможно с текущей версией Data.Conduit. Трубы не являются категориями, поэтому &&& не может быть и речи. И нет никакого способа, чтобы я мог думать о том, чтобы вытащить результаты с восходящего потока, постепенно довести их до обоих стоков и короткого замыкания, когда заканчивается первая раковина. (Хотя я не думаю, что Data.Conduit.Util.zipSinks замыкается таким образом, похоже, было бы очень желательно.) За исключением, конечно, соответствия шаблону на обоих Sinks (например, zipSinks в пакете), но это то, что мы пытаясь избежать здесь.

Это, пожалуйста, love, чтобы быть здесь ошибочным.


Это некрасиво, но вы можете сделать это в явном виде.

Первый импорт:

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) 

Теперь zipSinks. В принципе, вы хотите создать раковину, которая тянет входной сигнал вверх и отправляет его каждому приемнику отдельно. В этом случае я использовал CL.sourceList, чтобы сделать это. Если await возвращает Nothing, maybeToList возвращает пустой список, поэтому дочерние потоки также запускаются без ввода. Наконец, выход каждого дочернего приемника затем подается в кортеж.

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) 

Вот несколько примеров использования zipSinks. Кажется, что он отлично работает как внутри IO, так и за его пределами, и в нескольких тестах, которые я сделал, выход соответствует выходному сигналу zipped', созданному с использованием старого 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

Ницца! (NB, вы можете написать 'await >> = return. Fmap (2 *)' для 'doubleHead', и аналогичным образом' l <- fmap maybeToList await' вместо использования 'input' в' zipSinks'. Кроме того, Data.Conduit.Internals' посторонний импорт?) – huon

+0

Да, я понял, что я, вероятно, мог бы использовать функторы в некоторых местах. Я все еще достаточно n00b в Haskell, что, к сожалению, это, как правило, редактирование второго шага. И да, 'Data.Conduits.Internals' является посторонним. Первоначально я смотрел на него с помощью 'sinkToPipe'. Спасибо за указание на них. Я обновлю ответ. – Eric

+2

Ваша версия 'zipSinks' объединяет только первые элементы. Например, 'runResourceT $ CL.sourceList [1,2,3] $$ zipSinks (CL.take 2) (CL.take 2)' будет возвращать '([1], [1])', но должен '([1 , 2], [1,2]) '. – tymmym