如何修改状态monad?

时间:2018-05-04 02:18:42

标签: haskell

我使用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?

2 个答案:

答案 0 :(得分:1)

看起来你可以使用Network.AMQP.Lifted.consumeMsgsStateT s IOMonadBaseControl 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状态 - 回调被注册时的状态。

这意味着您可以使用它来传递全局配置状态,但以跟踪回调执行之间的状态。