如何修改状态单子?

3

我使用状态单子转换器来管理全局状态,如下所示:

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个回答

1
看起来你可以使用Network.AMQP.Lifted.consumeMsgsStateT s IOMonadBaseControl IO m的实例,因此您可以在单个runStateT中运行整个consumeMsgs
是的,StateT monad transformer基本上是纯代码的一种很好的符号表示法,因此如果您的API仅接受IO回调,则除了使用MVar或IORef等“真实”状态之外别无选择。

PS: 正如其他回答所建议的那样,Network.AMQP.Lifted.consumeMsgs回调函数中进行的状态更改不会传播到后续的回调运行或结果状态。我无法理解实现方式,但我尝试了一些liftBaseWith,它确实看起来是这样。


1
为了增加未来参考的澄清,被接受的答案并不完全正确。虽然 Network.AMQP.Lifted.consumeMsgs 应该可以与 StateT s IO 一起使用,但是 RabbitMQ Haskell 库实际上会在每次使用后丢弃单调状态。这意味着如果您确实使用了该实例,则不会看到在初始 consumeMsgs 调用之后所做的更改,包括回调本身所做的更改。回调基本上每次都使用相同的单调状态——在注册回调时的状态。
这意味着您可以使用它传递全局配置状态,但不能用它来跟踪回调执行之间的状态。

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