【问题标题】:Accessing an instance method embedded in the monad stack访问嵌入在 monad 堆栈中的实例方法
【发布时间】:2014-10-10 22:18:58
【问题描述】:

我已经在 monad 堆栈上定义了一个新类型包装器,但努力解决如何实现“已经”实现的 typeclass 方法。感觉应该很容易在ExceptionMonad 实例中为EngineM 使用Ghc 中的gmask 方法,但我无法理解必要的样板和提升以使其正常工作。有什么指点吗?

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 = ??

【问题讨论】:

  • 假设 RWST 有一个ExceptionMonad 的实例,然后将{-# LANGUAGE GeneralizedNewtypeDeriving #-} 放在你的模块顶部并写上newtype EngineM a = EngineM { ... } deriving (ExceptionMonad)
  • 我建议查看异常包。

标签: haskell


【解决方案1】:

我们可以通过为RWSTGhc 类型类MonadIOExceptionMonad 提供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

我们将保留您对EngineMderiving 的定义,我们感兴趣的类型类。

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.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。如果我们在这里开始编写函数定义,它会给我们一个r 和一个s,以供以后需要它们时使用。接下来我们遇到的是来自m (b, s, w)m,因此我们将工作交给了下属mask。我们现在需要提供一个函数,给定一种屏蔽异步错误的方法,返回一个m (b, s, w)。我们将调用屏蔽异步错误的方法restore。我们必须制作我们需要的b 的唯一方法是卡在f 中,它需要一种方法来屏蔽所有类型aRWST 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) 的小问题。如果我们有rsrunRWST 将从RWST r w s m b 得到m (b, s, w)。幸运的是,我们在最外层的函数周围还有一个额外的 rs,我们就完成了。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-06-15
    • 2018-03-17
    • 2014-05-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-25
    相关资源
    最近更新 更多