【发布时间】:2015-03-19 06:59:39
【问题描述】:
我正在实现一个 IRC 机器人,因为我使用 OpenSSL.Session 通过 SSL 进行连接,所以我使用 lazyRead 函数从套接字读取数据。在连接的初始阶段,我需要按顺序执行几件事:昵称协商、昵称服务识别、加入频道等)因此涉及一些状态。现在我想出了以下内容:
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 总是严格的?
【问题讨论】:
-
另一种选择是使用conduit。