从 pubSub 回调外部使用 Redis.Message

问题描述

documentation for Hedis中,给出了一个使用pubSub函数的例子:

pubSub :: PubSub -> (Message -> IO PubSub) -> Redis ()

pubSub (subscribe ["chat"]) $ \msg -> do
    putStrLn $ "Message from " ++ show (msgChannel msg)
    return $ unsubscribe ["chat"]

鉴于 pubSub 返回一个 Redis (),是否仍然可以在代码的更深处,从回调外部重新使用此 msg 消息?

我正在从在 pubSub monad 中运行的 Scotty 端点调用 ScottyM,并且应该返回(长话短说)一个 json msg

myEndpoint :: ScottyM ()
myEndpoint =
    post "/hello/world" $ do
        data :: MyData <- jsonData
        runRedis redisConn $ do
            pubSub (subscribe ["channel"]) $ \msg -> do
                doSomethingWith msg
                return $ unsubscribe ["channel"]

        -- how is it possible to retrieve `msg` from here?
        json $ somethingBuiltFromMsg

或者,有没有办法在回调中使用 Scotty 的 json?到目前为止,我还无法做到这一点。

解决方法

我假设您打算用 json 进一步缩进该行。

您可以在 IO 中使用可变变量,例如IORef

import Data.IORef (newIORef,writeIORef,readIORef)
import Control.Monad.IO.Class (liftIO)

myEndpoint :: ScottyM ()
myEndpoint =
    post "/hello/world" $ do
        data :: MyData <- jsonData
        msgRef <- liftIO (newIORef Nothing)
        runRedis redisConn $ do
            pubSub (subscribe ["channel"]) $ \msg -> do
                writeIORef msgRef (Just msg)
                return $ unsubscribe ["channel"]
        Just msg <- liftIO (readIORef msgRef)
        json $ doSomethingWithMsg msg

编辑:我想我真的不知道 runRedis 函数是否在收到消息之前阻塞,如果不是这种情况,那么您可以使用 MVar 代替:

import Control.Concurrent.MVar (putMVar,takeMVar,newEmptyMVar)
import Control.Monad.IO.Class (liftIO)

myEndpoint :: ScottyM ()
myEndpoint =
    post "/hello/world" $ do
        data :: MyData <- jsonData
        msgVar <- liftIO newEmptyMVar
        runRedis redisConn $ do
            pubSub (subscribe ["channel"]) $ \msg -> do
                putMVar msgVar msg
                return $ unsubscribe ["channel"]
        msg <- liftIO (takeMVar msgVar)
        json $ doSomethingWithMsg msg