【问题标题】:How to modify a state monad?如何修改状态单子?
【发布时间】:2018-05-04 02:18:41
【问题描述】:

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

【问题讨论】:

    标签: haskell


    【解决方案1】:

    看来你可以使用Network.AMQP.Lifted.consumeMsgsStateT s IOMonadBaseControl IO m 的一个实例,所以你可以在单个 runStateT 中运行整个 consumeMsgs

    是的,StateT monad 转换器基本上是纯代码的一个很好的符号,所以如果你的 API 只接受 IO 回调,你别无选择,只能使用“真实”状态,如 MVar 或 IORef 等。

    PS:正如other answer 所建议的,在Network.AMQP.Lifted.consumeMsgs 的回调中完成的状态更改不会传播到后续的回调运行或结果状态。我无法完全理解实现,但我尝试了liftBaseWith 一点,它看起来确实如此。

    【讨论】:

      【解决方案2】:

      为了添加可能对将来参考有用的说明,接受的答案并不准确。虽然 Network.AMQP.Lifted.consumeMsgs 应该与 StateT s IO 一起使用,但 RabbitMQ haskell 库实际上在每次使用后都会丢弃单子状态。这意味着如果您确实使用该实例,您将看不到在初始 consumeMsgs 调用之后所做的更改,包括回调本身所做的更改。回调基本上每次都以相同的 Monadic 状态调用 - 注册回调时的状态。

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-04-04
        • 1970-01-01
        • 2015-04-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-10-09
        相关资源
        最近更新 更多