Ich bin ein IRC-Bot implementieren und da ich über SSL mithilfe von OpenSSL.Session verbinden, verwende ich lazyRead
Funktion, um Daten aus dem Socket zu lesen. Während der Anfangsphase der Verbindung muss ich einige Dinge in der Reihenfolge ausführen: Nick-Verhandlung, NickServ-Identifikation, Verbinden von Kanälen usw.), so dass ein bestimmter Zustand involviert ist. Im Augenblick kam ich mit dem folgenden:Was ist eine idiomatische Art der Handhabung eines Lazy-Eingangskanal in 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)
Also, wenn ich auf den „Connected“ Zustand erhalte ich am Ende noch durch den Fall Aussage gehe, obwohl es eigentlich nur benötigt, um die Verbindung zu initialisieren. Das andere Problem ist, dass das Hinzufügen von verschachtelten StateTs sehr schmerzhaft wäre.
Eine andere Möglichkeit wäre, mapM
durch etwas zu ersetzen, das nur Linien verarbeitet, bis wir verbunden sind, und dann eine andere Schleife über den Rest zu starten. Dies würde erfordern, entweder zu verfolgen, was in der Liste übrig ist oder SSL.lazyRead
noch einmal aufzurufen (was nicht zu schlecht ist).
Eine andere Lösung besteht darin, die verbleibende Linienliste im Zustand zu halten und bei Bedarf Linien zu zeichnen, ähnlich wie getLine
.
Was ist in diesem Fall das Bessere? Würde Haskells Trägheit es so machen, dass wir direkt zu Connected
Fall gehen, nachdem der Staat aufhört zu aktualisieren oder ist case
immer streng?
Eine andere Alternative wäre [Conduit] (https://www.fpcomplete.com/school/to-infinity-and-beyond/pick-of-the-week/conduit-overview) zu verwenden. –