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

时间:2015-03-20 07:59:45

标签: haskell monads free-monad monadfix

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

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的{​​{1}}实例,但也没有运气 -

m

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


或者,在我的实际应用中,我甚至不需要使用mfix f = FreeT $ do rec ran <- runFreeT (f z) z <- case ran of Pure x -> return x Free iact -> -- ... return -- ... ...我可以使用FreeT;也就是说,Free只是一个monad而不仅仅是monad变换器,而且

Interactive

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

2 个答案:

答案 0 :(得分: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 .)

我们可以直接捕捉到这个&#34;了解互操作者&#34;没有事先提交翻译。

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

如果t是monad transormer,mMonadt m也是Monad,则UnFree t mMonad。给定一个解释器,我们可以将两个需要绑定器的计算绑定在一起。

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

用法

对于Interactive,您需要将FreeT打包在UnFreeT中。

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不再&#34;尽可能地释放解释器&#34;。解释者再也无法决定在任何地方做什么。 UnFreeT中的计算可以求助翻译。当计算需要并使用解释器时,将使用相同的解释器来解释程序中用于开始解释程序的部分。

答案 1 :(得分:2)

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

您的InteractiveF是经过深思熟虑的Moore machines的基础算符。摩尔机器提供输出,在您的情况下提示,然后根据输入确定下一个要做的事情,在您的情况下是环境。摩尔机器总是首先输出。

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

如果我们关注MonadFix关于为Free撰写Free (MooreF a b)个实例,但我们将自己约束于MonadFix的特定情况,我们最终会确定是否有{{1} Free (MooreF a b)的实例然后必须存在一个函数

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

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

与let in State

一样

如果您只提前阅读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的格式为f next = MooreF (f' next) g',其中f'g'是其他一些函数。

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)

我们将g绑定到g'

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'

almostMooreFFix的所有undefined甚至不需要let