首页 > 解决方案 > 如何使免费的 monad 解释器递归?

问题描述

在使用的Free monad 解释器iterT,我希望有一个内部状态,但我不知道该怎么做,因为据我了解,该函数提供了预加载递归调用iterT的延续。f我想StateT包装器是一种可能的解决方案(?),但如果可能的话最好避免。谢谢。

编辑:为了澄清,内部状态是mval传递给inner函数的参数。我想分配一个资源并继续使用该资源解释器。

import Control.Monad.Trans.Free.Church
import Control.Monad.Free.TH

type SomeFree m = FT SomeFreeF m
data SomeFreeF next = SomeAct (() -> next)
deriving instance (Functor SomeFreeF)
makeFree ''SomeFreeF

runSomeFree :: SomeFree IO () -> IO ()
runSomeFree = inner Nothing
  where
  inner mval =
    iterT \case
      SomeAct f -> do
        case mval of
          Nothing -> do
            a <- init
            inner (Just a) (FT someAct f ??)
                       -- How to continue the inner loop with    
                       -- the new state and the continuation `f`?
          Just a -> do
            f a

标签: haskellrecursionmonadsmonad-transformersfree-monad

解决方案


正如我在评论中指出的那样,乍一看,这似乎是一份工作iterTM,就像iterT它在您选择的 monad 转换器中运行一样。

iterTM :: (Functor f, Monad m, MonadTrans t, Monad (t m)) => (f (t m a) -> t m a) -> FreeT f m a -> t m a
iterTM f (FreeT m) = do  -- running in the output monad `t`
    val <- lift m
    case fmap (iterTM f) val of  -- fold the children first
        Pure x -> return x
        Free y -> f y

您可以选择输出 monad t,但maFreeT您折叠的数据结构定义。对于 的每一层FreeT,从底部开始,iterTM将折叠层的子层的完整结果传递f到您的回调中。您可以决定如何处理这些单子结果(在它们之间进行选择,对它们进行排序,等等)。

您提到在 a 中运行您的折叠StateT,但您提供的示例代码在我看来更像ReaderT。(您不会从每次迭代中返回修改后的状态 - 只是向下传递修改后的参数。)

runSomeFree :: Monad m => SomeFree m a -> ReaderT (Maybe ()) m a
runSomeFree = iterTM go
    where
        go (SomeAct f) = ask >>= \case
            Just () -> f ()
            Nothing -> local (const $ Just ()) (f ())

推荐阅读