提取monad构成作为变压器

时间:2011-09-14 08:42:42

标签: haskell monads composition monad-transformers

很抱歉,如果这个问题看起来有点微不足道......那不适合我。 我很乐意写下以下的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”的“提升”...我想自动推导可能有所帮助,但我想了解它在这种情况下是如何工作的。

提前致谢。

2 个答案:

答案 0 :(得分:10)

我不认为SbT的定义是你想要的。这定义了仿函数合成,并假设m参数是FunctorApplicative,这应该保留这些属性。但是,这样的构图一般不会从另外两个构成一个新的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变换器工作,隐藏了它实际上是一个复合的事实。

如果您愿意,通过解除组合变换器的实例,为MonadReaderMonadState编写实例应该很简单。

答案 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同义词通常更可取(因为它们必须始终“完全应用”);使用SBSBT执行此操作将如下所示:

type SB i = ReaderT (AlgRO i) (State (AlgState i))
type SBT m i = ReaderT (AlgRO i) (StateT (AlgState i) m)
相关问题