提取monad构成作为变压器

dsi*_*ign 5 monads haskell composition monad-transformers

对不起,如果问题看起来有点微不足道......那不适合我.我很乐意写下以下的monad:

type SB i a = ReaderT ( AlgRO i ) (State ( AlgState i ) ) a
Run Code Online (Sandbox Code Playgroud)

这是一个表现良好的monad.ReaderT是monad变换器,State是状态monad,AlgRO和AlgState分别是i中用于可变和只读状态的参数化数据类型.现在,如果我想用newtype制作一个整洁的monad变换器,就像这样:

newtype SbT m i a = SbT {
    runSbT:: m ( SB i a )
}
Run Code Online (Sandbox Code Playgroud)

我该怎么办?我甚至无法将绑定方法(Monad类型组合)放在一起,更不用说"MonadTrans"的"提升"...我想自动推导可能有所帮助,但我想了解它在这种情况下是如何工作的.

提前致谢.

C. *_*ann 10

我不认为这个定义SbT是你想要的.这定义了仿函数组合,并假设m参数是FunctorApplicative,这应该保留这些属性.但是,这样的构图一般不会从另外两个构成新的monad.有关主题的更多信息,请参阅此问题.

那么,如何你创建你想要的单子转换,然后呢?虽然monad不直接构成,但monad 变换器可以组成.因此,要从现有的变换器中构建一个新的变换器,您基本上只想给该组合命名.这与newtype你的不同,因为你m直接应用它,而不是将它传递给变换器堆栈.

关于定义monad变换器要记住的一件事是它们必然以某种方式"向后"工作 - 当你将复合变换器应用到monad时,"最里面的"变换器会获得第一个裂缝,并且变换monad变为monad变换器产生是下一个变压器可以使用的产品,&c.请注意,这与将组合函数应用于参数时获得的顺序没有任何不同,例如(f . g . h) x,将参数赋予hfirst,即使它f是组合中的"第一个"函数.

好了,你的组合变压器需要考虑它的应用到单子,并把它传递给最里面的变压器,这是,嗯....哎呀,原来,SB应用于单子.难怪这不起作用.首先,我们需要删除它.它在哪里?不 - State我们可以删除它,但我们不想,因为它是你想要的一部分.嗯,但是等一下 - 又被State定义为什么?哦耶:

type State s = StateT s Identity
Run Code Online (Sandbox Code Playgroud)

啊哈,我们去吧.让我们Identity从那里得到它.我们从你目前的定义出发:

type SB i a = ReaderT ( AlgRO i ) (State ( AlgState i ) ) a
Run Code Online (Sandbox Code Playgroud)

等效形式:

type SB i a = ReaderT ( AlgRO i ) ( StateT ( AlgState i ) Identity ) a
Run Code Online (Sandbox Code Playgroud)

然后我们踢出懒惰的屁股:

type SB' i m a = ReaderT ( AlgRO i ) ( StateT ( AlgState i ) m ) a
type SB i a = SB' i Identity a
Run Code Online (Sandbox Code Playgroud)

但现在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
Run Code Online (Sandbox Code Playgroud)

需要注意的runSbT几点:这里的函数不是字段访问器,而是我们知道的堆栈中每个变换器的组合"运行"函数.同样,该lift功能必须为两个内部变压器提升一次,然后添加最终newtype包装.这两个都使它作为单个monad变换器工作,隐藏了它实际上是一个复合的事实.

如果你愿意,就应该直接写入实例MonadReader,并MonadState为好,抬起对于组成的变压器的实例.