卡在国家莫纳德 [英] Stuck in the State Monad
问题描述
我想使用节点和唯一键的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 T
ransformer. 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 -}
如果t
是StateT 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)
请注意,我仍然使用get
和put
.这是因为transformers
包实现了我描述的所有概念,并将get
和put
的签名概括为:
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
这意味着您可以对State
和StateT
都使用get
和put
.
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屋!