我已经在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 = ??
答案 0 :(得分:1)
我们可以为RWST
类型Ghc
和MonadIO
的{{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.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
的{{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)
和runRWST
,m (b, s, w)
会从RWST r w s m b
获得r
。幸运的是,我们还有一个额外的s
和r
坐在最外面的功能区域,我们已经完成了。