很抱歉,如果这个问题看起来有点微不足道......那不适合我。 我很乐意写下以下的monad:
type SB i a = ReaderT ( AlgRO i ) (State ( AlgState i ) ) a
这是一个表现良好的monad。 ReaderT是monad变换器,State是状态monad,AlgRO和AlgState分别是i中用于可变和只读状态的参数化数据类型。现在,如果我想用newtype制作一个整洁的monad变换器,就像这样:
newtype SbT m i a = SbT {
runSbT:: m ( SB i a )
}
我该怎么办?我甚至无法将绑定方法(Monad类型组合)放在一起,更不用说“MonadTrans”的“提升”...我想自动推导可能有所帮助,但我想了解它在这种情况下是如何工作的。
提前致谢。
答案 0 :(得分:10)
我不认为SbT
的定义是你想要的。这定义了仿函数合成,并假设m
参数是Functor
或Applicative
,这应该保留这些属性。但是,这样的构图一般不会从另外两个构成一个新的monad。有关该主题的更多信息,请参阅this question。
那么,怎么做你创建了你想要的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
包装器。这两个都使它作为单个monad变换器工作,隐藏了它实际上是一个复合的事实。
如果您愿意,通过解除组合变换器的实例,为MonadReader
和MonadState
编写实例应该很简单。
答案 1 :(得分:2)
您打算在新类型的内容周围添加m
吗?我建议如下:
newtype Sb i a = Sb { runSb :: SB i a }
...这应该让你的instance Monad (Sb i)
更容易编写。如果你真的想写一个monad变压器,那么你应该一直使用变压器;例如,
type SBT m i a = ReaderT (AlgRO i) (StateT (AlgState i) m) a
newtype SbT m i a = SbT { runSbT :: SBT m i a }
作为第二个兴趣点,η-reduce type
同义词通常更可取(因为它们必须始终“完全应用”);使用SB
和SBT
执行此操作将如下所示:
type SB i = ReaderT (AlgRO i) (State (AlgState i))
type SBT m i = ReaderT (AlgRO i) (StateT (AlgState i) m)