You can use the Pipe type from pipes . The trick is that instead of creating a state machine and a transition function, you can code the state implicitly in the Pipe control flow.
Here's what Pipe looks like:
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
The stateful pipe corresponds to the part with the state of your processMessage function. It handles initialization and authentication, but defers further message processing to the next steps by returning msg .
You can then iterate over all the Pipe yield messages with 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)
Now all you need is a source of ByteString strings to feed to processMessage . You can use the following Producer :
lines :: Producer ByteString IO () lines = do bs <- liftIO (ByteString.getLine) if ByteString.null bs then return () else do yield bs lines
Then you can connect lines to processMessage and run them:
runEffect (lines >-> processMessage) :: IO ()
Note that the manufacturer of lines does not use lazy IO . It will work even if you use the strict ByteString module, but the behavior of the entire program will still be lazy.
If you want to know more about how pipes works, you can read the pipes tutorial .
source share