我不认为这个定义SbT
是你想要的。这定义了functor composition,并假设m
参数是 a Functor
or Applicative
,这应该保留这些属性。但一般来说,像这样的组合不会从其他两个单子中创建一个新的单子。有关该主题的更多信息,请参阅此问题。
那么,你如何创建你想要的 monad 转换器呢?虽然 monad 不能直接组合,但可以组合 monad转换器。所以要从现有的变压器中构建一个新的变压器,你基本上只是想给那个组合起一个名字。这与newtype
您所拥有的不同,因为您m
直接应用,而不是将其传递给变压器堆栈。
关于定义 monad 转换器要记住的一件事是,它们必须以某些方式“向后”工作——当你将复合转换器应用于 monad 时,“最里面”的转换器会首先破解它,然后转换后的 monad 它产生的是下一个变压器输出的东西,&c。请注意,这与将组合函数应用于参数时得到的顺序没有任何不同,例如(f . g . h) x
,将参数赋予h
第一个,即使f
是组合中的“第一个”函数。
好的,所以你的复合转换器需要将它应用到的 monad 传递给最里面的转换器,即,嗯.... 哎呀,原来它SB
已经应用于一个 monad。难怪这不起作用。我们需要先删除它。它在哪里?不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
。