Я использовал zipSinks :: Monad m => Sink i m r -> Sink i m r' -> Sink i m (r, r')
для этого, но считается устаревшим.
Каков предпочтительный способ объединения двух приемников?
Ответ 1
Edit
Рассмотрев это, я не думаю, что это возможно с текущей версией Data.Conduit. Трубы не являются категориями, поэтому &&&
не может быть и речи. И нет никакого способа, чтобы я мог думать о том, чтобы вытащить результаты из восходящего потока, поэтапно кормить их обе стоки и короткое замыкание, когда заканчивается первая раковина. (Хотя я не думаю, что Data.Conduit.Util.zipSinks
коротко замыкается таким образом, кажется, что это было бы очень желательно.) За исключением, конечно, соответствия шаблону на обоих Sinks (например, zipSinks
в пакете), но это что мы пытаемся избежать здесь.
Тем не менее, я был бы рад оказаться здесь неправильно.
Это некрасиво, но вы можете сделать это очевидным образом.
Первый импорт:
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)
Ответ 2
((Пакет conduit-0.5.2.3. Весь модуль предназначен только для обратной совместимости.) )
[ изменить]
Итак, моя простая монадическая догадка (ниже) кажется неправильной, хотя типы верны. Теперь я могу только догадываться, что ответ таков:
Заменяющие функции все еще находятся в разработке, в значительной степени похожи на все 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