在IO monad中使用一个monad

5

是否有类似于liftIO相反的东西?我正在使用WebSockets,并希望能够在单独的线程中监听来自服务器的消息。下面是我的代码:

import Network.WebSockets
import qualified Data.Text as T
import Control.Monad.IO.Class
import Control.Monad
import Control.Concurrent
import Control.Applicative

printMessages :: WebSockets Hybi00 ()
printMessages = forever $ do
    resp <- receiveDataMessage 
    liftIO $ print resp

run :: WebSockets Hybi00 ()
run = do
    liftIO . forkIO $ printMessages
    forever $ do
      line <- liftIO getLine
      sendTextData . T.pack $ line

main = connect "0.0.0.0" 8080 "/" run

因此printMessages监听服务器消息并将其打印出来。问题是,forkIO期望返回IO()的函数。有没有办法让我在IO单子中运行 printMessages


1
也许可以使用runWithSocket?通常,要从MonadIO m转换为IO,你需要一些runXY函数。 - Daniel Fischer
1个回答

5

如果我理解正确,你想在另一个线程中接收消息的原因是主线程将等待用户输入以发送。

文档来看,如果你反转线程的角色:在主线程中接收,异步地从其他线程发送,你会更容易实现。

然后,您可以使用getSink :: Protocol p => WebSockets p (Sink p)在分叉之前获取一个sink,然后可以使用sendSink :: Sink p -> Message p -> IO ()(位于IO中)与其一起使用,避免了混合monad的整个问题。

换句话说,将代码重构为以下内容:

sendMessages :: Sink Hybi00 -> IO ()
sendMessages sink = forever $ do
    line <- getLine
    let msg = textData . T.pack $ line
    sendSink sink msg

run :: WebSockets Hybi00 ()
run = do
    sink <- getSink
    liftIO . forkIO $ sendMessages sink
    forever $ do
      resp <- receiveDataMessage 
      liftIO $ print resp

main = connect "0.0.0.0" 8080 "/" run

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接