我使用State Monad Transformer来管理这样的全局状态
data State = State ...
StateT State IO ()
我使用amqp
来消费来自RabbitMQ的消息。将根据收到的消息修改状态。该函数具有类似
consumeMsgs :: Channel
-> Text
-> Ack
-> ((Message, Envelope) -> IO ()) -- ^ the callback function
-> IO ConsumerTag
现在我们可以忽略其他参数,但第三个是我将提供的回调函数以及修改发生的地方。
因为它主要是IO Monad,所以我使用这个函数如下
consumeMsgs chan queue Rmq.Ack (flip evalStateT ssss . rmqCallback)
这里ssss
是我放入的状态,我发现在回调函数rmqCallback
的过程中,状态可以被正确修改。但是,每次下一次回调发生时,全局状态都与调用consumeMsgs之前相同或等于ssss
。
我理解State Monad只是一个需要初始状态才能在整个过程中投入并保持状态的过程,但与Monad中的状态无关(我错过了什么?)所以我依靠MVar来保持并修改状态,这是有效的。我想知道它还有其他方法来处理这个,也许是另一个Monad?
答案 0 :(得分:1)
看起来你可以使用Network.AMQP.Lifted.consumeMsgs。 中运行整个StateT s IO
是MonadBaseControl IO m
的一个实例,因此您可以在单consumeMsgs
runStateT
是的,StateT monad转换器基本上是纯代码的一个很好的表示法,所以如果你的API只接受IO回调,你别无选择,只能使用" real"像MVar或IORef等国家。
PS:正如other answer所示,Network.AMQP.Lifted.consumeMsgs回调中完成的状态更改不会传播到后续回调运行或结果状态。我无法绕过实现,但我尝试了liftBaseWith,看起来确实如此。
答案 1 :(得分:1)
要添加可能对将来参考有用的说明,接受的答案并不准确。虽然Network.AMQP.Lifted.consumeMsgs
应该与StateT s IO
一起使用,但RabbitMQ haskell库实际上会在每次使用后丢弃monadic状态。这意味着如果您确实使用该实例,则不会看到在初始consumeMsgs
调用之后所做的更改,包括回调本身所做的更改。回调基本上每次调用具有相同的Monadic状态 - 回调被注册时的状态。
这意味着您可以使用它来传递全局配置状态,但不以跟踪回调执行之间的状态。