首页 文章

被困在州Monad

提问于
浏览
7

我想使用节点和唯一键的IntMap创建图形结构 . 这个主题已经涵盖herehere . 我理解状态monad是如何工作的,基本上将状态函数 - >(val,state)包装在newtype中,这样我们就可以为它创建一个monad实例 . 我已经阅读了很多相关主题 . 我仍然无法理解如何在程序执行过程中获得唯一(或仅增量)值 . 它's easy enough to get a run of successive IDs, but once I 1123624 to get out of the monad it seems like I'回到我开始的地方,我必须跟踪当前的ID . 我觉得我被卡在了monad中 . 我考虑的另一个选项是保持整个IntMap和当前的"next" ID作为状态,但这似乎非常"imperative"和极端 . This问题非常相似,但没有得到很多答案(或者我可能只是遗漏了一些明显的东西) . 在程序执行过程中利用状态monad获取唯一ID的惯用方法是什么?谢谢 .

1 回答

  • 10

    让我们想象一下我们要 IO -确认 State monad . 那会是什么样的?我们纯粹的 State monad只是一个新类型:

    s -> (a, s)
    

    那么,在返回最终值之前, IO 版本可能会产生一些副作用,如下所示:

    s -> IO (a, s)
    

    这种模式很常见,它有一个名字,特别是 StateT

    newtype StateT s m a = StateT { runStateT :: s -> m (a, s) }
    

    该名称末尾有 T ,因为它是monad T 变形金刚 . 我们将"base monad"和 StateT s m "transformed" monad称为 m .

    如果 mMonad ,则 StateT s m 仅为 Monad

    instance (Monad m) => Monad (StateT s m) where {- great exercise -}
    

    但是,除此之外,所有monad变换器都实现了 MonadTrans 类,定义如下:

    class MonadTrans t where
        lift :: (Monad m) => m a -> t m a
    
    instance MonadTrans (StateT s) where {- great exercise -}
    

    如果 tStateT s ,则 lift 的类型专门用于:

    lift :: m a -> StateT s m a
    

    换句话说,它让我们“提升”基础monad中的一个动作,成为变形monad中的一个动作 .

    因此,对于您的特定问题,您需要 StateT (IntMap k v) IO monad,它将 IO 扩展为 State . 然后你可以在这个monad中编写你的整个程序:

    main = flip runStateT (initialState :: IntMap k v) $ do
        m <- get        -- retrieve the map
        lift $ print m  -- lift an IO action
        (k, v) <- lift readLn
        put (insert k v m)
    

    请注意,我仍然使用 getput . 那是因为 transformers 包实现了我描述的所有概念,并且它将 getput 的签名概括为:

    get :: (Monad m) => StateT s m s
    put :: (Monad m) => s -> StateT s m ()
    

    这意味着它们会自动在 StateT 内工作 . transformers 然后只将 State 定义为:

    type State s = StateT s Identity
    

    这意味着您可以将 getput 用于 StateStateT .

    要了解有关monad变换器的更多信息,我强烈推荐Monad Transformers - Step by Step .

相关问题