这个Monad Stack功能的名称是什么?

And*_*rew 7 monads haskell state-monad monad-transformers

我在状态monad中有一堆有状态函数.程序中的某一点需要有一些IO操作,所以我将IO包装在StateT中,得到一对这样的类型:

mostfunctions :: State Sometype a
toplevel :: StateT Sometype IO a
Run Code Online (Sandbox Code Playgroud)

为了简单起见,我不想将IO上下文传递给主要的函数集,我想避免将它们包装在monad堆栈类型中.但是为了从顶层功能中调用它们,我需要类似于升力的东西,但我并不是想从内部单子中提升一个值.相反,我想将StateT monad中的状态转换为State monad中的等价物.要做到这一点,我有以下内容:

wrapST :: (State Sometype a) -> StateT Sometype IO a
wrapST f = do s <- get
              let (r,s2) = runState f s 
              put s2
              return r
Run Code Online (Sandbox Code Playgroud)

然后这用于交错,如下所示:

toplevel = do liftIO $ Some IO functions
              wrapST $ Some state mutations
              liftIO $ More IO functions
              ....
Run Code Online (Sandbox Code Playgroud)

它似乎是一个相当明显的代码块,所以我想知道这个函数是否有标准名称,它已经在标准库中的某个地方实现了?我试图保持描述简单,但显然这延伸到将一个变换器拉出堆栈,将包装的值转换为变换器类型的表兄弟,跳过堆栈中的下面的monad,然后将结果推回到结束.

fuz*_*fuz 9

重构代码以使用类型StateT SomeType m a而不是State SomeType a,因为第一个与任意monad堆栈兼容,这可能是个好主意.如果您像这样更改它,则不再需要函数wrapST,因为您可以直接调用有状态函数.

好的.假设你有一个功能subOne :: Monad m => State Int Int:

subOne = do a <- get
            put $ a - 1
            return a
Run Code Online (Sandbox Code Playgroud)

现在,改变类型的这样一个从所有功能State SomeType aStateT SomeType m a,留下m的是.这样,您的函数可以在任何monadic堆栈上工作.对于那些需要IO的函数,您可以指定底部的monad必须是IO:

printState :: MonadIO m => StateT Int m ()
printState = do a <- get
             liftIO $ print a
Run Code Online (Sandbox Code Playgroud)

现在,应该可以将两个函数一起使用:

-- You could use me without IO as well!
subOne :: Monad m => StateT Int m ()
subOne = do a <- get
            put $ a - 1

printState :: MonadIO m => StateT Int m ()
printState = do a <- get
             liftIO $ print a

toZero :: StateT Int IO ()
toZero = do subOne     -- A really pure function
            printState -- function may perform IO
            a <- get
            when (a > 0) toZero
Run Code Online (Sandbox Code Playgroud)

PS:我使用GHC 7,一些libs在中途发生了变化,因此在GHC 6上可能会有所不同.

  • 对于`printState`和`toZero`,允许任何monad可能是好的,只要IO位于堆栈的底部,即`printState :: MonadIO m => StateT Int m()`,或者只是删除类型签名:) (2认同)
  • 旁注:在最新的Haskell平台中,State是根据StateT定义的,我认为所有可以推广到monad变换器的monad都是如此.直到最近,情况并非如此.这并没有改变答案的正确性:仍然应该使用StateT,因为它更通用. (2认同)