Wdrażam bota IRC i ponieważ łączę się przez SSL za pomocą OpenSSL.Session używam funkcji lazyRead
do odczytu danych z gniazda. W początkowej fazie połączenia muszę wykonać kilka rzeczy w kolejności: negocjowanie nicków, identyfikacja nickserv, łączenie kanałów itd., Więc jest w to zaangażowany jakiś stan. Teraz wpadłem na następujący:Co to jest idiomatyczny sposób obsługi leniwego kanału wejściowego w Haskell
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)
Więc kiedy się do stanu „Connected” I nadal skończyć się poprzez zestawienie przypadku nawet jeśli jest to tylko naprawdę potrzebne, aby zainicjować połączenie. Innym problemem jest to, że dodanie zagnieżdżonych StateT byłoby bardzo bolesne.
Innym sposobem byłoby zastąpienie mapM
czymś niestandardowym, aby przetwarzać tylko linie, dopóki nie zostaniemy połączeni, a następnie uruchomimy kolejną pętlę nad resztą. Wymagałoby to albo śledzenia tego, co pozostało na liście, albo ponownego wywołania SSL.lazyRead
(co nie jest złe).
Innym rozwiązaniem jest pozostawienie listy pozostałych wierszy w stanie i narysowanie linii w razie potrzeby podobnej do getLine
.
Co jest lepsze w tym przypadku? Czy lenistwo Haskella sprawi, że przejdziemy bezpośrednio do sprawy Connected
po tym, jak stan przestanie się odnawiać lub będzie zawsze surowy?
Inną alternatywą byłoby użycie [conduit] (https://www.fpcomplete.com/school/to-infinity-and-beyond/pick-of-the-week/conduit-overview). –