monad 转换器堆栈中的 evalState

evalState in a monad transformer stack

给定一个 mtl monad 堆栈,例如ExceptT String (WriterT String (State s a)),如何在不需要解包外部 monad 的情况下评估内部状态 monad?

have :: ExceptT String (WriterT String (State s)) a
f    :: State s a -> a

want :: ExceptT String (WriterT String Identity) a

我可以通过调用 runExceptT 然后调用 runWriterT 然后重新打包结果来完成此操作,但这似乎是错误的方法。


据我所试,fmap 或类似的东西不会起作用,因为 monad 转换器堆栈被视为一个完整的 monad。我需要的是 "split" monad 转换器堆栈的功能,如下所示:

split :: (MonadTrans s, Monad t) => (s t) a -> s (t a)

要么我没有找到这个函数,要么解决方案完全不同。

我不知道像你的 split 这样普遍的事情,但使用 map...T 函数可以防止显式操纵:

have :: ExceptT String (WriterT String (State s)) a
f    :: State s a -> a

want :: ExceptT String (WriterT String Identity) a
want = mapExceptT (mapWriterT (return . f)) have

在这种特殊情况下,似乎最简单的方法是使用 ExceptT eWriterT w 转换器的 MFunctor 实例:

import Control.Monad.Morph

floop :: Monad m
      => s
      -> ExceptT e (WriterT w (StateT s m)) a
      -> ExceptT e (WriterT w m) a
floop s = hoist (hoist $ flip evalStateT s)

State s = StateT s Identity 以来,上面的轻微概括是立竿见影的。