gpt4 book ai didi

haskell - 将 monad 组合抽象为变压器

转载 作者:行者123 更新时间:2023-12-03 01:40:18 26 4
gpt4 key购买 nike

抱歉,如果这个问题看起来有点微不足道……它不适合我。我很高兴地编写了以下 monad:

type SB i a = ReaderT ( AlgRO i ) (State ( AlgState i ) ) a

这是一个表现良好的单子(monad)。 ReaderT 是一个 monad 转换器,State 是 State monad,AlgRO 和 AlgState 是 i 中参数化的数据类型,分别表示可变和只读状态。现在,如果我想用 newtype 制作一个简洁的 monad 转换器,如下所示:

newtype SbT m i a = SbT {
runSbT:: m ( SB i a )
}

我应该如何进行?我什至无法设法将绑定(bind)方法(Monad 类型类)组合在一起,更不用说“提升”(MonadTrans)了……我想自动推导可能会有所帮助,但我想了解它在这种情况下是如何工作的。

提前致谢。

最佳答案

我不认为 SbT 的定义就是你想要的。这定义了仿函数组合,并假设 m参数是 FunctorApplicative ,这应该保留这些属性。但一般来说,这样的组合不会从另外两个单子(monad)中创建一个新的单子(monad)。请参阅this question了解有关该主题的更多信息。

那么,您如何创建您想要的 monad 转换器呢?虽然 monad 不能直接组合,但 monad transformers 可以组合。因此,要从现有的变压器中构建一个新的变压器,您本质上只需要为该组合命名即可。这与 newtype 不同你有,因为你正在应用 m直接,而不是将其传递到变压器堆栈。

关于定义 monad 变压器需要记住的一件事是它们必然以某些方式“向后”工作——当你将复合变压器应用于 monad 时,“最里面的”变压器首先受到影响,然后它产生的转换后的单子(monad)是下一个变压器要使用的,等等。请注意,这与将组合函数应用于参数时获得的顺序没有任何不同,例如(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 wrapper 。这两者都使它作为单个 monad 转换器工作,隐藏了它实际上是一个复合体的事实。

如果您愿意,为 MonadReader 编写实例应该很简单和MonadState同样,通过提升组合变压器的实例。

关于haskell - 将 monad 组合抽象为变压器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7413545/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com