我不认为SbT 的定义是你想要的。这定义了函子组合,并假设m 参数是Functor 或Applicative,这应该保留这些属性。但一般来说,像这样的组合不会从其他两个单子中创建一个新的单子。有关该主题的更多信息,请参阅this question。
那么,如何创建你想要的 monad 转换器呢?虽然 monad 不直接组合,但可以组合 monad transformers。所以要从现有的变压器中构建一个新的变压器,你基本上只是想给那个组合起一个名字。这与您拥有的 newtype 不同,因为您直接应用 m,而不是将其传递到转换器堆栈。
在定义 monad 转换器时要记住的一点是,它们必须以某些方式“向后”工作 - 当您将复合转换器应用到 monad 时,“最里面”的转换器会首先破解它,然后它产生的转换后的 monad 是下一个转换器可以使用的,&c。请注意,这与将组合函数应用于参数时得到的顺序没有任何不同,例如(f . g . h) x 首先将参数传递给 h,即使 f 是组合中的“第一个”函数。
好的,所以你的复合转换器需要将它应用到的 monad 传递给最里面的转换器,也就是说,嗯.... 哎呀,原来SB 已经应用于单子。难怪这不起作用。我们需要先删除它。它在哪里?不是State--我们可以删除它,但我们不想这样做,因为它是你想要的一部分。嗯,但是等等——State 又被定义为什么?哦对了:
type State s = StateT s Identity
啊哈,我们走了。让我们把Identity 拿出来。我们从您当前的定义出发:
type SB i a = ReaderT ( AlgRO i ) (State ( AlgState i ) ) a
到等价形式:
type SB i a = ReaderT ( AlgRO i ) ( StateT ( AlgState i ) Identity ) a
然后我们把这个懒惰的家伙踢出去:
type SB' i m a = ReaderT ( AlgRO i ) ( StateT ( AlgState i ) m ) a
type SB i a = SB' i Identity a
但是现在SB' 看起来有点像一个monad 转换器定义,并且有充分的理由,因为它是。所以我们重新创建了newtype 包装器,并在那里抛出了一些实例:
newtype SbT i m a = SbT { getSB :: ReaderT ( AlgRO i ) ( StateT ( AlgState i ) m ) a }
instance (Functor m) => Functor (SbT i m) where
fmap f (SbT sb) = SbT (fmap f sb)
instance (Monad m) => Monad (SbT i m) where
return x = SbT (return x)
SbT m >>= k = SbT (m >>= (getSB . k))
instance MonadTrans (SbT i) where
lift = SbT . lift . lift
runSbT :: SbT i m a -> AlgRO i -> AlgState i -> m (a, AlgState t)
runSbT (SbT m) e s = runStateT (runReaderT m e) s
需要注意的几点:这里的runSbT 函数不是字段访问器,而是我们所知道的堆栈中每个转换器的组合“运行”函数。同样,lift 函数必须为两个内部转换器提升一次,然后添加最终的 newtype 包装器。这两者都使它作为一个单子转换器工作,隐藏了它实际上是一个复合的事实。
如果您愿意,也可以直接为 MonadReader 和 MonadState 编写实例,方法是提升组合转换器的实例。