gpt4 book ai didi

haskell - 在 Haskell 中处理惰性输入 channel 的惯用方法是什么

转载 作者:行者123 更新时间:2023-12-04 15:10:27 25 4
gpt4 key购买 nike

我正在实现一个 IRC 机器人,因为我使用 OpenSSL.Session 通过 SSL 进行连接,所以我使用 lazyRead函数从套接字读取数据。在连接的初始阶段,我需要按顺序执行几件事:昵称协商、昵称服务识别、加入 channel 等)因此涉及一些状态。现在我想出了以下内容:

data ConnectionState = Initial | NickIdentification | Connected

listen :: SSL.SSL -> IO ()
listen ssl = do
lines <- BL.lines `fmap` SSL.lazyRead ssl
evalStateT (mapM_ (processLine ssl) lines) Initial

processLine :: SSL.SSL -> BL.ByteString -> StateT ConnectionState IO ()
processLine ssl line = do case message of
Just a -> processMessage ssl a
Nothing -> return ()
where message = IRC.decode $ BL.toStrict line

processMessage :: SSL.SSL -> IRC.Message -> StateT ConnectionState IO ()
processMessage ssl m = do
state <- S.get
case state of
Initial -> when (IRC.msg_command m == "376") $ do
liftIO $ putStrLn "connected!"
liftIO $ privmsg ssl "NickServ" ("identify " ++ nick_password)
S.put NickIdentification
NickIdentification -> do
when (identified m) $ do
liftIO $ putStrLn "identified!"
liftIO $ joinChannel ssl chan
S.put Connected
Connected -> return ()
liftIO $ print m
when (IRC.msg_command m == "PING") $ (liftIO . pong . mconcat . map show) (IRC.msg_params m)

因此,当我进入“已连接”状态时,我仍然会通过 case 语句,即使它只需要初始化连接。另一个问题是添加嵌套的 StateT 会非常痛苦。

其他方法是替换 mapM有一些自定义的东西只处理线,直到我们连接,然后在其余部分开始另一个循环。这将需要跟踪列表中剩余的内容或调用 SSL.lazyRead再一次(这还不错)。

另一种解决方案是将剩余线条列表保持在状态并在需要时绘制线条,类似于 getLine .

在这种情况下,更好的做法是什么? Haskell的懒惰会让我们直接去 Connected吗?状态停止更新或为 case 后的情况总是严格?

最佳答案

您可以使用 Pipepipes 输入.诀窍是,您可以在 Pipe 的控制流中隐式编码状态,而不是创建状态机和转换函数。 .

这是Pipe看起来像:

stateful :: Pipe ByteString ByteString IO r
stateful = do
msg <- await
if (IRC.msg_command msg == "376")
then do
liftIO $ putStrLn "connected!"
liftIO $ privmsg ssl "NickServ" ("identify " ++ nick_password)
yield msg
nick
else stateful

nick :: Pipe ByteString ByteString IO r
nick = do
msg <- await
if identified msg
then do
liftIO $ putStrLn "identified!"
liftIO $ joinChannel ssl chan
yield msg
cat -- Forward the remaining input to output indefinitely
else nick
stateful管道对应于您的 processMessage 的有状态部分功能。它处理初始化和身份验证,但通过重新 yield 将进一步的消息处理推迟到下游阶段。使用 msg .

然后,您可以遍历 Pipe 中的每条消息 yield使用 for :
processMessage :: Consumer ByteString IO r
processMessage = for stateful $ \msg -> do
liftIO $ print m
when (IRC.msg_command m == "PING") $ (liftIO . pong . mconcat . map show) (IRC.msg_params m)

现在您需要的只是 ByteString 的来源要馈送到 processMessage 的行.您可以使用以下 Producer :
lines :: Producer ByteString IO ()
lines = do
bs <- liftIO (ByteString.getLine)
if ByteString.null bs
then return ()
else do
yield bs
lines

然后可以连接 linesprocessMessage并运行它们:
runEffect (lines >-> processMessage) :: IO ()

请注意 lines生产者不使用懒 IO .即使您使用严格的 ByteString,它也会起作用模块,但整个程序的行为仍然会是惰性的。

如果您想了解更多 pipes作品,你可以阅读 the pipes tutorial .

关于haskell - 在 Haskell 中处理惰性输入 channel 的惯用方法是什么,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29138827/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com