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
参数是Functor
或Applicative
,这应该保留这些属性.但是,这样的构图一般不会从另外两个构成新的monad.有关该主题的更多信息,请参阅此问题.
那么,如何做你创建你想要的单子转换,然后呢?虽然monad不直接构成,但monad 变换器可以组成.因此,要从现有的变换器中构建一个新的变换器,您基本上只想给该组合命名.这与newtype
你的不同,因为你m
直接应用它,而不是将它传递给变换器堆栈.
关于定义monad变换器要记住的一件事是它们必然以某种方式"向后"工作 - 当你将复合变换器应用到monad时,"最里面的"变换器会获得第一个裂缝,并且变换monad变为monad变换器产生是下一个变压器可以使用的产品,&c.请注意,这与将组合函数应用于参数时获得的顺序没有任何不同,例如(f . g . h) x
,将参数赋予h
first,即使它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
为好,抬起对于组成的变压器的实例.