如何使免费的 monad 解释器递归?
How to make a free monad interpreter recursive?
在使用 iterT
的 Free monad interpreter 中,我想要一个内部状态,但我不确定该怎么做,因为 iterT
函数提供了延续f
预加载了递归调用,据我所知。我想 StateT
包装器是一个可能的解决方案(?),但如果可能的话最好避免。谢谢。
编辑:澄清一下,内部状态是传递给 inner
函数的 mval
参数。我想分配一个资源并继续解释该资源。
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
正如我在评论中指出的那样,乍一看这似乎是 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
,但是 m
和 a
是由您折叠起来的 FreeT
数据结构定义的。对于 FreeT
的每一层,从底部开始,iterTM
传递一个 f
,其中包含将层的子层折叠到您的回调中的完整结果。您可以决定如何处理这些单子结果(在它们之间进行选择,对它们进行排序,等等)。
您提到 运行 您在 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 ())
我接受本杰明的答案是正确的,因为它可能是对这个问题最简单/最好的答案,但我最终发现 FT
monad 并没有促进我的用例容易地。然而,operational
和 monad-skeleton
确实允许这种用例 quite easily,因为它们不通过绑定函数线程化解释器。与后者一起工作也很有趣。
在使用 iterT
的 Free monad interpreter 中,我想要一个内部状态,但我不确定该怎么做,因为 iterT
函数提供了延续f
预加载了递归调用,据我所知。我想 StateT
包装器是一个可能的解决方案(?),但如果可能的话最好避免。谢谢。
编辑:澄清一下,内部状态是传递给 inner
函数的 mval
参数。我想分配一个资源并继续解释该资源。
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
正如我在评论中指出的那样,乍一看这似乎是 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
,但是 m
和 a
是由您折叠起来的 FreeT
数据结构定义的。对于 FreeT
的每一层,从底部开始,iterTM
传递一个 f
,其中包含将层的子层折叠到您的回调中的完整结果。您可以决定如何处理这些单子结果(在它们之间进行选择,对它们进行排序,等等)。
您提到 运行 您在 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 ())
我接受本杰明的答案是正确的,因为它可能是对这个问题最简单/最好的答案,但我最终发现 FT
monad 并没有促进我的用例容易地。然而,operational
和 monad-skeleton
确实允许这种用例 quite easily,因为它们不通过绑定函数线程化解释器。与后者一起工作也很有趣。