как добавить новый источник внутри канала haskell

У меня возникла проблема со следующим кодом, использующим network-conduit:

import Data.Conduit.List as CL
import Data.Conduit.Text as CT
import qualified Data.ByteString.Char8 as S8
import qualified Data.Text as TT

mySource :: ResourceT m => Integer -> Source m Int
mySource i = {- function -} undefined

myApp :: Application
myApp src snk = 
    src $= CT.decode CT.ascii
        $= CL.map decimal
        $= CL.map {-problem here-}
        $$ src

в проблемном месте я хочу написать что-то вроде

\t -> case t of
    Left err = S8.pack $ "Error:" ++ e
    Right (i,xs) = (>>>=) mySource 
                 {- or better: 
                   do 
                   (>>>=) mySource
                   (<<<=) T.pack xs
                  -}

где функция (>>>=) переводит вывод mySource на следующий уровень, а (<<<=) отправляет функцию обратно на предыдущий уровень


person qnikst    schedule 10.02.2012    source источник
comment
Поддерживает ли conduit отправку данных вверх по течению? Я знаю, что pipes знает. Возможно, вам нужно будет использовать pipes для вашего варианта использования.   -  person Cactus    schedule 30.03.2016
comment
Conduit может поместить данные обратно на свой вход с помощью leftover, но здесь Conduit хочет поместить данные обратно на вход вышестоящего Conduit, что невозможно. В этой ситуации следует использовать парсер Conduit.   -  person pat    schedule 28.11.2017


Ответы (1)


Сеть нарезает поток байтов на произвольные ByteString фрагменты. В приведенном выше коде эти фрагменты ByteString будут сопоставлены с фрагментами Text, а каждый фрагмент Text будет проанализирован как decimal. Однако строка десятичных цифр, представляющая один decimal, может быть разделена на два (или более) фрагмента Text. Кроме того, как вы понимаете, использование decimal возвращает вам оставшуюся часть фрагмента Text, который не был проанализирован как часть decimal, который вы пытаетесь вернуть во входной поток.

Обе эти проблемы можно решить, используя Data.Conduit.Attoparsec. conduitParserEither с Data.Attoparsec.Text.decimal. Обратите внимание, что недостаточно просто проанализировать decimal; вам также нужно будет обрабатывать какой-то разделитель между decimals.

Также невозможно соединить Source с CL.map, так как сигнатура типа CL.map

map :: Monad m => (a -> b) -> Conduit a m b

Функция, которую вы передаете map, получает возможность преобразовать каждый вход a в один выход b, а не в поток b. Для этого вы можете использовать awaitForever, но вам нужно преобразовать Source в обычный Producer с toProducer, чтобы типы совпадали.

Однако в вашем коде вы пытаетесь отправить ошибки синтаксического анализа вниз по течению как ByteString, но вывод mySource как Int, что является ошибкой типа. Вы должны предоставить поток ByteString в обоих случаях; успешный случай синтаксического анализа может вернуть Conduit, созданный путем слияния других Conduit, если он заканчивается выходом ByteString:

...
$= (let f (Left err) = yield $ S8.pack $ "Error: " ++ show err
        f (Right (_, i)) = toProducer (mySource i) $= someOtherConduit
    in awaitForever f)

где someOtherConduit погружает Int из mySource и создает ByteString.

someOtherConduit :: Monad m => Conduit Int m ByteString

Наконец, я полагаю, вы хотели подключить snk в конце трубы вместо src.

person pat    schedule 27.11.2017
comment
Код писался для conduit-0.4 или что-то в этом роде, где Producers, Consumers и Pipes вообще не было, остатков тоже не было. У меня нет этой проблемы вокруг меня в течение 5 лет или более, и я не уверен, смогу ли я вспомнить, почему я хотел решить эту проблему таким образом, и если ответ охватывает проблему, которая у меня была на самом деле. Однако он говорит правильные вещи, поэтому может быть полезен для людей, использующих каналы в наши дни, поэтому я отмечаю его как принятый. - person qnikst; 04.12.2017