FreeT生成的解释器monad变换器的MonadFix实例?

Jus*_* L. 7 monads haskell monadfix free-monad

我有一个简化版本的标准解释器monad变换器生成FreeT:

data InteractiveF p r a = Interact p (r -> a)

type Interactive p r = FreeT (InteractiveF p r)
Run Code Online (Sandbox Code Playgroud)

p是"提示",r是"环境"......人们可以使用以下方式运行:

runInteractive :: Monad m => (p -> m r) -> Interactive p r m a -> m a
runInteractive prompt iact = do
  ran <- runFreeT iact
  case ran of
    Pure x -> return x
    Free (Interact p f) -> do
      response <- prompt p
      runInteractive prompt (f resp)

instance MonadFix m => MonadFix (FreeT (InteractiveF p r)) m a)
mfix = -- ???
Run Code Online (Sandbox Code Playgroud)

我觉得这种类型或多或少只是一个受约束的版本StateT...如果有的话,Interactive p r IO我认为是IO... 的约束版本......我想......但是......好吧,无论如何,我的直觉告诉我应该有一个很好的例子.

我尝试写一个,但我似乎无法弄明白.到目前为止,我最接近的尝试是:

mfix f = FreeT (mfix (runFreeT . f . breakdown))
  where
    breakdown :: FreeF (InteractiveF p r) a (FreeT (InteractiveF p r) m a) -> a
    breakdown (Pure x) = x
    breakdown (Free (Interact p r)) = -- ...?
Run Code Online (Sandbox Code Playgroud)

我也试过使用一个版本利用的MonadFix实例m,但也没有运气 -

mfix f = FreeT $ do
  rec ran <- runFreeT (f z)
      z   <- case ran of
               Pure x -> return x
               Free iact -> -- ...
  return -- ...
Run Code Online (Sandbox Code Playgroud)

任何人都知道这是否真的可行,或者为什么不是?如果是的话,我继续观看的好地方是什么?


或者,在我的实际应用中,我甚至不需要使用FreeT......我可以使用Free; 也就是说,Interactive只是一个单子而不仅仅是一个单子变换器,并且有

runInteractive :: Monad m => (p -> m r) -> Interactive p r a -> m a
runInteractive _ (Pure x) = return x
runInteractive prompt (Free (Interact p f) = do
    response <- prompt p
    runInteractive prompt (f response)
Run Code Online (Sandbox Code Playgroud)

如果这种情况有可能而不是一般的FreeT案例,我也会很高兴:)

Cir*_*dec 5

想象一下,你已经有了翻译Interactive.

interpret :: FreeT (InteractiveF p r) m a -> m a
interpret = undefined
Run Code Online (Sandbox Code Playgroud)

编写一个MonadFix实例是微不足道的:

instance MonadFix m => MonadFix (FreeT (InteractiveF p r) m) where
    mfix = lift . mfix . (interpret .)
Run Code Online (Sandbox Code Playgroud)

我们可以直接捕捉到"了解互操作者"的想法,而无需提前向翻译提交.

{-# LANGUAGE RankNTypes #-}

data UnFreeT t m a = UnFree {runUnFreeT :: (forall x. t m x -> m x) -> t m a}
--   given an interpreter from `t m` to `m` ^                          |
--                                  we have a value in `t m` of type a ^
Run Code Online (Sandbox Code Playgroud)

UnFreeT只是一个ReaderT读取翻译的人.

如果t是monad变压器,UnFreeT t也是monad变压器.我们可以轻松地UnFreeT从一个计算中构建一个不需要知道解释器的计算,只需忽略该插入器.

unfree :: t m a -> UnFreeT t m a
--unfree = UnFree . const
unfree x = UnFree $ \_ -> x

instance (MonadTrans t) => MonadTrans (UnFreeT t) where
    lift = unfree . lift
Run Code Online (Sandbox Code Playgroud)

如果t是monad transormer,m是a Monad,而且t m也是a Monad,那么UnFree t m就是a Monad.给定一个解释器,我们可以将两个需要绑定器的计算绑定在一起.

{-# LANGUAGE FlexibleContexts #-}

refree :: (forall x. t m x -> m x) -> UnFreeT t m a -> t m a
-- refree = flip runUnFreeT
refree interpreter x = runUnFreeT x interpreter

instance (MonadTrans t, Monad m, Monad (t m)) => Monad (UnFreeT t m) where
    return = lift . return
    x >>= k = UnFree $ \interpreter -> runUnFreeT x interpreter >>= refree interpreter . k
Run Code Online (Sandbox Code Playgroud)

最后,给定解释器,只要底层monad具有MonadFix实例,我们就可以修复计算.

instance (MonadTrans t, MonadFix m, Monad (t m)) => MonadFix (UnFreeT t m) where
    mfix f = UnFree $ \interpreter -> lift . mfix $ interpreter . refree interpreter . f
Run Code Online (Sandbox Code Playgroud)

一旦我们有了解释器,我们实际上可以做基础monad可以做的任何事情.这是因为,一旦我们有了,interpreter :: forall x. t m x -> m x我们可以做以下所有事情.我们可以从去m x通过t m x所有的方式来UnFreeT t m x和回来了.

                      forall x.
lift               ::           m x ->         t m x
unfree             ::         t m x -> UnFreeT t m x
refree interpreter :: UnFreeT t m x ->         t m x
interpreter        ::         t m x ->           m x
Run Code Online (Sandbox Code Playgroud)

用法

对于你的Interactive,你会包裹FreeTUnFreeT.

type Interactive p r = UnFreeT (FreeT (InteractiveF p r))
Run Code Online (Sandbox Code Playgroud)

你的口译员仍然会写作出来FreeT (InteractiveF p r) m a -> m a.要将新Interactive p r m a的一直解释为m a你使用的

interpreter . refree interpreter
Run Code Online (Sandbox Code Playgroud)

UnFreeT不再是"解放了解释尽可能".解释者再也无法决定在任何地方做什么.计算中UnFreeT可以求解释.当计算需要并使用解释器时,将使用相同的解释器来解释程序的那部分,就像用于开始解释程序一样.