我已经定义了一个简单的 monad 转换器,EntityBuilderT
它只是一个 newtype over ReaderT
。
data EntityBuilderState = ...
newtype EntityBuilderT m a = EntityBuilderT (ReaderT EntityBuilderState m a)
为了将函数包装在新的“环境”中,我编写了以下组合器:
withNewSource :: (Monad m) => String -> EntityBuilderT m a -> EntityBuilderT m a
withNewSource itemId builder = ...
在某些情况下,我还想构建一个更大的变压器堆栈。例如:
f :: MaybeT (EntityBuilderT m) a
显然,我不能应用withNewSource
这个函数f
,因为 monad 类型不再匹配。因此,我尝试使用monad-control
这种组合器来编写新版本。
到目前为止,我编写的代码如下所示。尽管实例定义似乎没问题,但编译器 (GHC 7.4.1) 拒绝代码并显示以下消息:
Couldn't match type `IO' with `EntityBuilderT m0'
When using functional dependencies to combine
MonadBaseControl IO IO,
arising from the dependency `m -> b'
in the instance declaration in `Control.Monad.Trans.Control'
MonadBaseControl (EntityBuilderT m0) IO,
arising from a use of `control'
In the expression: control
In the expression: control $ \ run -> withNewSource itemId (run m)
我有些失落。任何人都明白真正的问题是什么?
{-# LANGUAGE FlexibleInstances, GeneralizedNewtypeDeriving,
MultiParamTypeClasses, TypeFamilies, UndecidableInstances #-}
import Control.Applicative (Applicative)
import Control.Monad (liftM)
import Control.Monad.Base
import Control.Monad.Trans (MonadTrans)
import Control.Monad.Trans.Control
import Control.Monad.Trans.Maybe (MaybeT)
import Control.Monad.Trans.Reader (ReaderT, withReaderT)
data EntityBuilderState
newtype EntityBuilderT m a = EntityBuilderT { unEB :: ReaderT EntityBuilderState m a }
deriving (Applicative, Functor, Monad, MonadTrans)
instance MonadBase b m => MonadBase b (EntityBuilderT m) where
liftBase = liftBaseDefault
instance MonadTransControl EntityBuilderT where
newtype StT EntityBuilderT a = StEB { unStEB :: StT (ReaderT EntityBuilderState) a }
liftWith f = EntityBuilderT $ liftWith $ \run ->
f $ liftM StEB . run . unEB
restoreT = EntityBuilderT . restoreT . liftM unStEB
instance MonadBaseControl b m => MonadBaseControl b (EntityBuilderT m) where
newtype StM (EntityBuilderT m) a = StMT { unStMT :: ComposeSt EntityBuilderT m a }
liftBaseWith = defaultLiftBaseWith StMT
restoreM = defaultRestoreM unStMT
withNewSource :: (Monad m) => String -> EntityBuilderT m a -> EntityBuilderT m a
withNewSource itemId (EntityBuilderT m) = EntityBuilderT (withReaderT undefined m)
withNewSource' :: String -> MaybeT (EntityBuilderT IO) a -> MaybeT (EntityBuilderT IO) a
withNewSource' itemId m = control $ \run -> withNewSource itemId (run m)