如何在管道haskell内添加新源

发布于 2025-01-03 22:47:33 字数 984 浏览 2 评论 0原文

我在使用 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 输出推到下一个级别的内容,并且 (<<<=) 正在将函数发送回上一级

I have a problem with the following code using 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

in problem place I want to write something like

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

where the (>>>=) function pushes mySource output to the next level and
(<<<=) is sending function back to previous level

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

生活了然无味 2025-01-10 22:47:33

网络将字节流分割成任意的ByteString块。通过上面的代码,这些 ByteString 块将被映射到 Text 块,并且每个 Text 块将被解析为 十进制。但是,表示单个小数的十进制数字字符串可以分为两个(或更多)Text块。此外,正如您所意识到的,使用十进制可以返回未解析为十进制部分的剩余文本块,您正试图将其推回到输入流中。

这两个问题都可以通过使用 Data.Conduit.Attoparsec 来解决。 PipelineParserEither 与 Data.Attoparsec.Text.decimal。请注意,仅解析十进制是不够的;您还需要处理小数点之间的某种分隔符。

也不可能从 CL.map 拼接 Source,因为 CL.map 的类型签名是

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

您传递给 < code>map 有机会将每个输入 a 转换为单个输出 b,而不是 b 的流。为此,您可以使用 awaitForever,但您需要使用 toProducerSource 转换为通用 Producer code> 以使类型匹配。

但是,在您的代码中,您尝试将解析错误作为 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)

其中 < code>someOtherConduit 从 mySource 接收 Int,并源 ByteString

someOtherConduit :: Monad m => Conduit Int m ByteString

最后,我相信您的意思是连接管道末端的 snk 而不是 src

The network chops up the byte stream into arbitrary ByteString chunks. With the code above, those ByteString chunks will be mapped to chunks of Text, and each chunk of Text will be parsed as a decimal. However, a string of decimal digits representing a single decimal may be split across two (or more) Text chunks. Also, as you realize, using decimal gives you back the remainder of the Text chunk that didn't get parsed as part of the decimal, which you are trying to shove back into the input stream.

Both of these problems can be solved by using Data.Conduit.Attoparsec. conduitParserEither with Data.Attoparsec.Text.decimal. Note that it is not sufficient to just parse decimal; you will also need to handle some kind of separator between decimals.

It is also not possible to splice a Source from CL.map, since CL.map's type signature is

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

The function you pass to map gets an opportunity to transform each input a into a single output b, not a stream of b's. To do that, you can use awaitForever, but you'll need to transform your Source into a general Producer with toProducer in order for the types to match.

However, in your code, you are trying to send parse errors downstream as ByteString's, but the output of mySource as Int's, which is a type error. You must provide a stream of ByteString in both cases; the successful parse case can return a Conduit made by fusing other Conduit's as long as it ends up with an output of ByteString:

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

where someOtherConduit sinks the Int's from mySource, and sources ByteString's.

someOtherConduit :: Monad m => Conduit Int m ByteString

Finally, I believe you meant to connect the snk at the end of the pipe instead of the src.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文