访问嵌入在monad堆栈中的实例方法

时间:2014-10-10 22:18:59

标签: haskell

我已经在monad堆栈上定义了一个newtype包装器,但是很难弄清楚如何实现"已经"实现了类型类方法。感觉应该很容易使用gmask Ghc实例ExceptionMonad中的EngineM方法,但我无法绕过必要的样板并提升以使其正常工作。有什么指针吗?

newtype EngineM a = EngineM { _runEngineM :: RWST EngineEnv EngineLog EngineState Ghc a }

instance ExceptionMonad Ghc where ...
-- already implemented

instance ExceptionMonad EngineM where
  gmask :: ((m a -> m a) -> m b) -> m b
  gmask f = ??

1 个答案:

答案 0 :(得分:1)

我们可以为RWST类型GhcMonadIO的{​​{1}} orphan instances提供更轻松的功能。这适用于从变换器构建解释器的一般策略 - 为每个属性编写类,实现每个变换器如何保留该属性,并使用

派生最终变换器堆栈上的类。
ExceptionMonad

我将ghc API{-# LANGUAGE GeneralizedNewtypeDeriving #-} 的所有导入作为前缀,这样我就可以保持原状。

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) Ghc.MonadIO提供RWST实例,lift来自Control.Monad.Trans.Class。我们需要这个,因为Ghc.MonadIOGhc.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 bgmask需要提供一种方法来屏蔽任意计算中的异步错误,这样做可以将依赖于该能力的值转换为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。如果我们在这里开始编写函数定义,它会在我们需要它们时为我们提供rs。接下来我们遇到的是来自m的{​​{1}},因此我们将工作交给了基础m (b, s, w)。我们现在需要提供一个函数,给定一种掩盖异步错误的方法,返回mask。我们将调用掩盖异步错误的方法m (b, s, w)。我们必须使restore成为我们需要的唯一方法是b,它希望有一种方法可以屏蔽所有类型f类型RWST r w s m a -> RWST r w s m a的异步错误。 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)runRWSTm (b, s, w)会从RWST r w s m b获得r。幸运的是,我们还有一个额外的sr坐在最外面的功能区域,我们已经完成了。