Я использовал zipSinks :: Monad m => Sink i m r -> Sink i m r' -> Sink i m (r, r')
для этого, но считается устаревшим.Каков предпочтительный способ объединения двух раковин?
ответ
((Пакет 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
Типы - это нормально, но не семантика. Ваша версия 'zipSinks' будет последовательно запускать раковины, а первая раковина будет полностью потреблять источник. – tymmym
Редактировать
После рассмотрения этого я не думаю, что это возможно с текущей версией 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)
Ницца! (NB, вы можете написать 'await >> = return. Fmap (2 *)' для 'doubleHead', и аналогичным образом' l <- fmap maybeToList await' вместо использования 'input' в' zipSinks'. Кроме того, Data.Conduit.Internals' посторонний импорт?) – huon
Да, я понял, что я, вероятно, мог бы использовать функторы в некоторых местах. Я все еще достаточно n00b в Haskell, что, к сожалению, это, как правило, редактирование второго шага. И да, 'Data.Conduits.Internals' является посторонним. Первоначально я смотрел на него с помощью 'sinkToPipe'. Спасибо за указание на них. Я обновлю ответ. – Eric
Ваша версия 'zipSinks' объединяет только первые элементы. Например, 'runResourceT $ CL.sourceList [1,2,3] $$ zipSinks (CL.take 2) (CL.take 2)' будет возвращать '([1], [1])', но должен '([1 , 2], [1,2]) '. – tymmym
Какое поведение, * точно *, вы хотите иметь «комбинированные» раковины? Я попытался взглянуть на старую документацию и реализацию 'zipSinks', но поведение было легко различимо с первого взгляда. –
@DanBurton: 'zipSinks' берет два раковины и возвращает раковину, которая создает пару с результатами соответствующих Sinks. Например, 'sizeCrc32Sink = zipSinks sizeSink crc32Sink' будет считать размер и контрольную сумму. Я то же самое, что описано Олегом [здесь] (http://okmij.org/ftp/Streams.html#1enum2iter). – tymmym
Хорошо, я вижу; он в основном подключает ожидание и подает выходной сигнал вверх по потоку одновременно на оба приемника, что приводит к разнесению входного потока в два. В документах для Data.Conduit.Util говорится, что «теперь есть более простые способы обработки их случаев использования», но я не вижу более простого способа для этого варианта использования, поскольку для этого требуется вникание в внутренние оболочки кабелепровода. –