假设您已经有一名口译员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
读取解释器。
If 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
If t
是一个 Monad 转换器,m
is a Monad
, and t m
也是一个Monad
, then UnFree t m
is 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
最后,给定解释器,只要底层 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 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
Usage
为您Interactive
,你会包裹FreeT
in 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
The UnFreeT
不再“尽可能地释放口译员”。口译员不能再随心所欲地决定要做什么。中的计算UnFreeT
可以请求翻译。当计算请求并使用解释器时,将使用与开始解释程序相同的解释器来解释程序的该部分。