7

我有一个由以下生成的标准解释器单子变换器的简化版本FreeT

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

type Interactive p r = FreeT (InteractiveF p r)

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 = -- ???

我觉得这种类型或多或少只是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)) = -- ...?

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

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

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


或者,在我的实际应用程序中,我什至不需要使用FreeT...我可以使用Free; 也就是说,haveInteractive只是一个 monad 而不仅仅是一个 monad 转换器,并且有

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)

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

4

2 回答 2

5

想象一下,您已经有一个Interactive.

interpret :: FreeT (InteractiveF p r) m a -> m a
interpret = undefined

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

instance MonadFix m => MonadFix (FreeT (InteractiveF p r) m) where
    mfix = lift . mfix . (interpret .)

我们可以直接捕捉到这种“了解解释者”的想法,而无需提前指定解释器。

{-# 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 ^

UnFreeT只是一个ReaderT读解释器。

如果t是一个单子变压器,UnFreeT t也是一个单子变压器。我们可以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

如果t是一个单子变压器,m是一个Monadt m也是一个Monad,那么UnFree t m是一个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

最后,给定解释器,只要底层 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

一旦我们有了解释器,我们实际上可以做任何底层 monad 可以做的事情。这是因为,一旦我们有了一个,interpreter :: forall x. t m x -> m x我们就可以做以下所有事情。我们可以从上到下m x再往下走。t m xUnFreeT 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

用法

对于您的Interactive,您将包裹FreeTUnFreeT.

type Interactive p r = UnFreeT (FreeT (InteractiveF p r))

您的解释器仍会被编写为生成FreeT (InteractiveF p r) m a -> m a. 将新Interactive p r m a的一直解释为m a您将使用的

interpreter . refree interpreter

不再“UnFreeT尽可能地释放解释器”。解释器不再可以随意决定在任何地方做什么。中的计算UnFreeT可以求翻译。当计算请求并使用解释器时,将使用与开始解释程序时相同的解释器来解释程序的那部分。

于 2015-03-21T18:11:32.687 回答
2

无法编写MonadFix m => MonadFix (Interactive p r)实例。

InteractiveF是经过充分研究的摩尔机器的基本函子。摩尔机器提供输出,在您的情况下为提示,然后根据输入(在您的情况下为环境)确定下一步要做的事情。摩尔机总是先输出。

data MooreF a b next = MooreF b (a -> next)
    deriving (Functor)

如果我们遵循CA McCann关于编写MonadFixfor 实例的论点,Free但将自己限制在 的特定情况下Free (MooreF a b),我们最终将确定如果有 for 的MonadFix实例,Free (MooreF a b)则必须存在一个函数

mooreFfix :: (next -> MooreF a b next) -> MooreF a b next

要编写这个函数,我们必须构造一个MooreF b (f :: a -> next). 我们没有任何b要输出的 s。可以想象,b如果我们已经有了下一个,我们可以得到一个a,但摩尔机器总是先输出。

就像让在州

mooreFfix如果你只是a在前面读一篇,你可以写一些接近的东西。

almostMooreFfix :: (next -> MooreF a b next) -> a -> MooreF a b next
almostMooreFfix f a = let (MooreF b g) = f (g a)
                      in (MooreF b g)

f那么,能够g独立于论点进行确定就变得势在必行了next。所有可能f使用的 s 都具有f next = MooreF (f' next) g'wheref'和is g'some other functions 的形式。

almostMooreFFix :: (next -> b) -> (a -> next) -> a -> MooreF a b next
almostMooreFFix f' g' a = let (MooreF b g) = f (g a)
                          in (MooreF b g)
                          where
                              f next = MooreF (f' next) g'

f通过一些等式推理,我们可以在右侧替换let

almostMooreFFix :: (next -> b) -> (a -> next) -> a -> MooreF a b next
almostMooreFFix f' g' a = let (MooreF b g) = MooreF (f' (g a)) g'
                          in (MooreF b g)

我们绑定gg'.

almostMooreFFix :: (next -> b) -> (a -> next) -> a -> MooreF a b next
almostMooreFFix f' g' a = let (MooreF b _) = MooreF (f' (g' a)) g'
                          in (MooreF b g')

当我们绑定b到变得不必要并且函数没有递归结时。f' (g' a)let

almostMooreFFix :: (next -> b) -> (a -> next) -> a -> MooreF a b next
almostMooreFFix f' g' a = MooreF (f' (g' a)) g'

所有不需要的almostMooreFFixesundefined甚至都不需要let.

于 2015-03-21T03:09:11.673 回答