卡在国家莫纳德 [英] Stuck in the State Monad

查看:107
本文介绍了卡在国家莫纳德的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想使用节点和唯一键的IntMap创建图形结构.在此处这个问题非常相似,但没有得到很多答案(或者我只是想念一些东西明显的).在整个程序执行过程中,利用状态monad获得唯一ID的惯用方式是什么?谢谢.

I want to create a graph structure using an IntMap of nodes and unique keys. This topic has been covered well here and here. I understand how the state monad works by basically wrapping a function of state -> (val,state) in a newtype so we can create a monad instance for it. I have read about the topic quite a bit. I still cant seem to get my head around how I can get unique (or just incremental) values throughout the execution of my program. It's easy enough to get a run of successive IDs, but once I "runState" to get out of the monad it seems like I'm back where I started with having to keep track of the current ID. I feel like I'm stuck in the monad. The other option I considered was to keep the entire IntMap and current "next" ID as the state, but that seems very "imperative" and extreme. This question is very similar but did not get many answers(or maybe I'm just missing something obvious). What is the idiomatic way to utilize the state monad to get unique ID throughout a program execution? Thanks.

推荐答案

让我们想象一下我们要IO-化State monad.那会是什么样?我们纯净的State monad只是周围的新类型:

Let's imagine we were to IO-ify the State monad. What would that look like? Our pure State monad is just a newtype around:

s -> (a, s)

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

Well, the IO version might do a little bit of side effects before returning the final values, which would look like:

s -> IO (a, s)

这种模式非常普遍,它有一个名称,特别是StateT:

That pattern is so common it has a name, specifically StateT:

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

该名称的末尾带有T,因为它是monad T转换程序.我们将m称为基本单子",而将StateT s m称为转换后的"单子.

The name has a T at the end because it is a monad Transformer. We call m the "base monad" and StateT s m the "transformed" monad.

StateT s m仅是Monad:

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

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

However, in addition to that, all monad transformers implement the MonadTrans class, defined as follows:

class MonadTrans t where
    lift :: (Monad m) => m a -> t m a

instance MonadTrans (StateT s) where {- great exercise -}

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

If t is StateT s, then lift's type specializes to:

lift :: m a -> StateT s m a

换句话说,它使我们可以举升"基本单声道中的一个动作,使其成为转换后的单声道中的一个动作.

In other words, it lets us "lift" an action in the base monad to become an action in the transformed monad.

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

So, for your specific problem, you want the StateT (IntMap k v) IO monad, which extends IO with additional State. Then you can write your entire program in this 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的签名概括为:

Notice that I still use get and put. That's because the transformers package implements all the concepts I described and it generalizes the signatures of get and put to be:

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

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

That means they automatically work within StateT. transformers then just defines State as:

type State s = StateT s Identity

这意味着您可以对StateStateT都使用getput.

That means that you can use get and put for both State and StateT.

要了解有关monad变压器的更多信息,我强烈建议 Monad变压器-分步进行步骤.

To learn more about monad transformers, I highly recommend Monad Transformers - Step by Step.

这篇关于卡在国家莫纳德的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆