我们可以通过为RWST 为Ghc 类型类MonadIO 和ExceptionMonad 提供orphan instances 来简化此操作。这符合从转换器构建解释器的一般策略 - 为每个属性编写类,实现每个转换器如何保留该属性,并使用
在最终转换器堆栈上派生类
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
我将为来自ghc API 的所有导入添加前缀Ghc,这样我就可以保持它们的正确性。
import Control.Applicative
import Data.Monoid
import Control.Monad.Trans.Class
import Control.Monad.Trans.RWS.Strict
import qualified Exception as Ghc
import qualified GhcMonad as Ghc
import qualified MonadUtils as Ghc
我们将保留您对EngineM、deriving 的定义,我们感兴趣的类型类。
newtype EngineM a = EngineM { _runEngineM :: RWST EngineEnv EngineLog EngineState Ghc.Ghc a }
deriving (Ghc.ExceptionMonad, Ghc.MonadIO, Monad, Applicative, Functor)
根据Control.Monad.Trans.Class 中的lift,为RWST 创建一个Ghc.MonadIO 实例很简单。我们需要这个,因为Ghc.MonadIO 是Ghc.ExceptionMonad 的超类。
instance (Monoid w, Ghc.MonadIO m) => Ghc.MonadIO (RWST r w s m) where
liftIO = lift . Ghc.liftIO
Ghc.ExceptionMonad 实例更棘手。你可能已经注意到RWST 带有一个liftCatch 函数,用于提升Ghc.gcatch 之类的东西。大部分问题在于提供gmask 定义。
instance (Monoid w, Ghc.ExceptionMonad m) => Ghc.ExceptionMonad (RWST r w s m) where
gcatch = liftCatch Ghc.gcatch
gmask f = RWST $ \r s -> Ghc.gmask $ \restore -> runRWST (f (mapRWST restore)) r s
解释起来有点难,所以我先把涉及到的类型都写出来
liftMask :: (((m (a, s, w) -> m (a, s, w)) -> m (b, s, w)) -> m (b, s, w)) ->
((RWST r w s m a -> RWST r w s m a) -> RWST r w s m b) -> RWST r w s m b
liftMask mask f = RWST $ \r s -> mask $ \restore -> runRWST (f (mapRWST restore)) r s
这里的想法是,给定一种在任意计算m a -> m a 中屏蔽异步错误的方法,我们有一个值m b。因此,此类值的整个类型为(m a -> m a) -> m b。 gmask 需要提供一种方法来屏蔽任意计算中的异步错误,并且这样做可以将依赖于该能力的值转换为简单的 m b。这就是为什么gmask 具有卷积类型((m a -> m a) -> m b) -> m b。
由于计算结果必须是m b,或者在我们的例子中是RWST r w s m b,我们可以从返回一个的构造函数开始。 RWST :: r -> s -> m (b, s, w) -> RWST r w s m b。如果我们在这里开始编写函数定义,它会给我们一个r 和一个s,以供以后需要它们时使用。接下来我们遇到的是来自m (b, s, w) 的m,因此我们将工作交给了下属mask。我们现在需要提供一个函数,给定一种屏蔽异步错误的方法,返回一个m (b, s, w)。我们将调用屏蔽异步错误的方法restore。我们必须制作我们需要的b 的唯一方法是卡在f 中,它需要一种方法来屏蔽所有类型a 的RWST r w s m a -> RWST r w s m a 类型的异步错误。 mapRWST 将来自m (a, s, w) -> m (a, s, w) 的函数转换为来自RWST r w s m a -> RWST r w s m a 的函数,并且gmask 承诺restore 是一种屏蔽来自m a1 -> m a1 的所有类型a1 的异步错误的方法,所以mapRWST restore 是正是我们需要传递给f 的屏蔽异步错误的方法。现在我们面临f 返回RWST r w s m b 但我们需要m (b, s, w) 的小问题。如果我们有r 和s,runRWST 将从RWST r w s m b 得到m (b, s, w)。幸运的是,我们在最外层的函数周围还有一个额外的 r 和 s,我们就完成了。