将 monad 组合抽象为变压器
抱歉,如果这个问题看起来有点微不足道……它不适合我。 我很高兴地编写了以下 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 )
}
我应该如何进行?我什至无法设法将绑定方法(Monad 类型类)组合在一起,更不用说“提升”(MonadTrans)了……我想自动推导可能会有所帮助,但我想了解它在这种情况下是如何工作的。
提前致谢。
Sorry if the question seems a bit trivial... it is not for me.
I have happily composed the following monad:
type SB i a = ReaderT ( AlgRO i ) (State ( AlgState i ) ) a
which is, well, a well behaved monad. ReaderT is a monad transformer and State is the State monad, and AlgRO and AlgState are datatypes parametrized in i for mutable and read-only state, respectively. Now, if I want to make of that a neat monad transformer with newtype, something like this:
newtype SbT m i a = SbT {
runSbT:: m ( SB i a )
}
how should I proceed? I can not even manage to put together the bind method (of Monad typeclass), much less "lift" (of MonadTrans)... I guess that automatic derivation could help, but I want to understand how it works in this case.
Thanks in advance.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
我认为
SbT
的定义不是您想要的。它定义了函子组合,并假设 m 参数是函子或 Applicative,这应该保留这些属性。但一般来说,这样的组合不会从另外两个单子中创建一个新的单子。有关该主题的更多信息,请参阅此问题。那么,如何创建您想要的 monad 转换器呢?虽然 monad 不能直接组合,但 monad transformers 可以组合。因此,要从现有的变压器中构建一个新的变压器,您本质上只需要为该组合命名即可。这与您拥有的
newtype
不同,因为您直接应用m
,而不是将其传递到变压器堆栈。关于定义 monad 变压器需要记住的一件事是,它们必然以某些方式“向后”工作——当你将复合变压器应用于 monad 时,“最里面的”变压器首先破解它,并且转换后的 monad产生的是下一个变压器要使用的内容,&c。请注意,这与将组合函数应用于参数时获得的顺序没有任何不同,例如
(f . g . h) x
将参数赋予h
首先,尽管f
是组合中的“第一个”函数。好的,所以你的复合变压器需要获取它所应用的 monad 并将其传递给最里面的变压器,也就是,呃......哎呀,原来
SB
已经了 应用于 monad。难怪这不起作用。我们需要首先删除它。它在哪里?不是State
——我们可以删除它,但我们不想这样做,因为它是您想要的一部分。嗯,但是等等——State
又被定义为什么呢?哦,是的:啊哈,我们开始了。让我们从那里获取
Identity
。我们从您当前的定义:到等效的形式:
然后我们把懒惰的人踢出去:
但现在 SB' 看起来可疑地像一个 monad 转换器定义,并且有充分的理由,因为它确实是。因此,我们重新创建
newtype
包装器,并抛出一些实例:需要注意的几件事:这里的newtype 包装器。这两者都使它作为单个 monad 转换器工作,隐藏了它实际上是一个复合体的事实。
runSbT
函数不是字段访问器,而是一个为我们所知的堆栈中的每个变压器组成“运行”函数。同样,lift
函数必须为两个内部变压器提升一次,然后添加最终的如果您愿意,通过提升组合变压器的实例,为
MonadReader
和MonadState
编写实例也应该很简单。I don't think that definition for
SbT
is what you want. That defines functor composition, and assuming them
parameter is aFunctor
orApplicative
, this should preserve those properties. But composition like that does not, in general, create a new monad out of two others. See this question for more on that subject.So, how do you create the monad transformer you want, then? While monads don't compose directly, monad transformers can be composed. So to build a new transformer out of existing ones, you essentially just want to give a name to that composition. This differs from the
newtype
you have because there you're applying them
directly, instead of passing it in to the transformer stack.One thing to keep in mind about defining monad transformers is that they necessarily work "backwards" in certain ways--when you apply a composite transformer to a monad, the "innermost" transformer gets the first crack at it, and the transformed monad it produces is what the next transformer out gets to work with, &c. Note that this isn't any different from the order you get when applying a composed function to an argument, e.g.
(f . g . h) x
gives the argument toh
first, even thoughf
is the "first" function in the composition.Okay, so your composite transformer needs to take the monad it's applied to and pass it to the innermost transformer, which is, uhm.... oops, turns out that
SB
is already applied to a monad. No wonder this wasn't working. We'll need to remove that, first. Where is it? NotState
--we could remove that, but we don't want to, because it's part of what you want. Hmm, but wait--what isState
defined as, again? Oh yeah:Aha, there we go. Let's get that
Identity
out of there. We go from your current definition:To the equivalent form:
Then we kick the lazy bum out:
But now
SB'
looks suspiciously like a monad transformer definition, and with good reason, because it is. So we recreate thenewtype
wrapper, and toss a few instances out there:A couple things to take note of: The
runSbT
function here is not the field accessor, but rather a composed "run" function for each transformer in the stack that we know of. Similarly, thelift
function has to lift once for the two inner transformers, then add the finalnewtype
wrapper. Both of these make it work as a single monad transformer, hiding the fact that it's actually a composite.If you'd like, it should be straightforward to write instances for
MonadReader
andMonadState
as well, by lifting the instances for the composed transformers.您是否打算在新类型中添加一个额外的
m
内容?我建议如下:...这应该使您的
实例 Monad (Sb i)
更容易编写。如果你真的想编写一个 monad 转换器,那么你应该一直使用转换器;例如,作为第二个兴趣点,它通常比 η-reduce
type
同义词更可取(因为它们必须始终“完全应用”);使用SB
和SBT
执行此操作将如下所示:Did you intend to wrap an additional
m
around things in your newtype? I would suggest the following:...which should make your
instance Monad (Sb i)
a bit easier to write. If you're really trying to write a monad transformer, then you should use transformers all the way down; for example,As a second point of interest, it's often preferable to η-reduce
type
synonyms (since they must always be "fully applied"); doing this withSB
andSBT
would look like this: