通常说来,monad变压器是否由附加条件引起的? [英] Do monad transformers, generally speaking, arise out of adjunctions?

查看:98
本文介绍了通常说来,monad变压器是否由附加条件引起的?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

Simon C在 伴随函子确定monad变压器,但升力在哪里? 我们的建筑...

In Adjoint functors determine monad transformers, but where's lift?, Simon C has shown us the construction...

newtype Three u f m a = Three { getThree :: u (m (f a)) }

...,作为那里讨论的答案,可以给它一个instance Adjunction f u => MonadTrans (Three u f)(附件

... which, as the answers there discuss, can be given an instance Adjunction f u => MonadTrans (Three u f) (adjunctions provides it as AdjointT). Any Hask/Hask adjunction thus leads to a monad transformer; in particular, StateT s arises in this manner from the currying adjunction between (,) s and (->) s.

我的后续问题是:此构造是否可以推广到其他monad变压器?例如,是否有一种方法可以从 transformers 程序包中从合适的附件中派生其他变压器?

My follow-up question is: does this construction generalise to other monad transformers? Is there a way to derive, say, the other transformers from the transformers package from suitable adjunctions?

元备注:我的回答最初是为西蒙·C的问题写的.我选择将其分解为一个自我回答的问题,因为在重读该问题时,我注意到我所谓的答案与那里的评论中的讨论有关,而不是与问题主体本身有关.此问与答也可以跟进的另外两个密切相关的问题是 是否有一个单子不 是不是由任意的组成?遍历始终为monad的monad?

Meta remarks: my answer here was originally written for Simon C's question. I opted to spin it off into a self-answered question because, upon rereading that question, I noticed my purported answer had more to do with the discussion in the comments over there than with the question body itself. Two other closely related questions, to which this Q&A arguably is also a follow-up, are Is there a monad that doesn't have a corresponding monad transformer (except IO)? and Is the composition of an arbitrary monad with a traversable always a monad?

推荐答案

此答案中的三种构造也可以以可重复的形式以 西蒙C的构造 ...

newtype Three u f m a = Three { getThree :: u (m (f a)) }

...依赖fu作为Hask endofunctors的伴随者.在StateT的情况下可以解决此问题,但要使其更笼统,我们必须处理两个相关的问题:

... relies on f and u being adjoint Hask endofunctors. While that works out in the case of StateT, there are two connected issues we have to deal with if we are to make it more general:

  • 首先,我们需要为变压器将要构建的特征单子"找到合适的附加条件;和

  • Firstly, we need to find suitable adjunctions for the "feature monads" the transformers will be built upon; and

第二,如果这种附加条件使我们远离Hask,我们将不得不以某种方式解决无法直接使用Hask monad m的事实.

Secondly, if such an adjunction takes us away from Hask, we will have to somehow workaround the fact that it won't be possible to use the Hask monad m directly.

我们可以尝试使用许多有趣的附件.特别是,每个单核都可以使用两种附加语:Kleisli附加语和Eilenberg-Moore附加语(有关它们的详细分类,请参见Emily Riehl,

There are quite a few interesting adjunctions we might experiment with. In particular, there are two adjunctions that are available for every monad: the Kleisli adjunction, and the Eilenberg-Moore adjunction (for a fine categorical presentation of them, see Emily Riehl, Category Theory In Context, section 5.2). In the categorical excurse that takes up the first half or so of this answer, I will focus on the Kleisli adjunction, simply because it is more comfortable to wiggle about in pseudo-Haskell.

(通过伪Haskell,我的意思是在后面的内容中会普遍使用符号.为了使您更容易理解,我将使用一些特殊的约定:|->表示不存在的事物之间的映射必需的类型;类似地,:表示类似于类型签名的内容; ~>表示非Hask形态;大括号和尖括号突出显示了选定的非Hask类别中的对象; .表示函子组成;以及表示FU是伴随函子.)

(By pseudo-Haskell, I mean there will be rampant abuse of notation in what follows. To make it easier on the eyes, I will use some ad hoc conventions: |-> means a mapping between things that aren't necessarily types; similarly, : means something that resembles a type signature; ~> means a non-Hask morphism; curly and angled brackets highlight objects in selected non-Hask categories; . also means functor composition; and F -| U means F and U are adjoint functors.)

如果g是Hask Monad,则在FK g之间有一个对应的Kleisli附加语FK g -| UK g,将我们带到g的Kleisli类别...

If g is a Hask Monad, there is a corresponding Kleisli adjunction FK g -| UK g between FK g, which takes us to the g's Kleisli category...

-- Object and morphism mappings.
FK g : a          |-> {a}
       f : a -> b |-> return . f : {a} ~> {b} ~ a -> g b
-- Identity and composition in Kleisli t are return and (<=<)

...和UK g,将我们带回到Hask:

... and UK g, which takes us back to Hask:

UK g : {a}            |-> g a
       f : {a} -> {b} |-> join . fmap f : g a -> g b  -- that is, (>>= f)

-- The adjunction isomorphism:
kla : (FK g a ~> {b}) -> (a -> UK g {b})
kra : (a -> UK g {b}) -> (FK g a ~> {b})
-- kla and kra mirror leftAdjunct and rightAdjunct from Data.Functor.Adjunction.
-- The underlying Haskell type is a -> g b on both sides, so we can simply have:
kla = id
kra = id

沿着Simon C的Three行,让我们以g作为特征monad,在其上构建变压器.遵循常规的Haskell术语,该转换器将以某种方式合并另一个Hask monad的效果,m我有时将其称为基本monad".

Along the lines of Simon C's Three, let's have g as the feature monad, on which the transformer will be built upon. The transformer will somehow incorporate the effects of another Hask monad, m, which I will sometimes refer to as the "base monad", following customary Haskell terminology.

如果尝试在FK gUK g之间挤压m,则会遇到上述第二个问题:我们需要一个Kleisli- g endofunctor,而不是Hask.除了弥补之外,别无他法.那样的话,我的意思是我们可以为函子定义函子(更具体地说,是两类endofunctors之间的函子),这有望将m变成我们可以使用的函子.我将其称为更高"的函子HK g.将其应用于m应该会给出以下信息:

If we attempt to squeeze m between FK g and UK g, we run into the second issue mentioned above: we would need a Kleisli-g endofunctor, rather than a Hask one. There is little else to do but make it up. By that, I mean we can define a functor for functors (more specifically, a functor between the two categories of endofunctors) which will hopefully turn m into something we can use. I will call this "higher" functor it HK g. Applying it to m should give something like this:

-- Keep in mind this is a Kleisli-g endofunctor.
HK g m : {a}                |-> {m a}
         f : {a} ~> {b}     |-> kmap f : {m a} ~> {m b} ~ m a -> g (m b)
-- This is the object mapping, taking functors to functors.
-- The morphism mapping maps natural transformations, a la Control.Monad.Morph:
         t : ∀x. m x -> n x |-> kmorph t : ∀x. {m x} ~> {n x} ~ ∀x. m x -> g (n x)
-- I won't use it explicitly, but it is there if you look for it.

克莱斯里上的克莱斯里

(注意:漫长的分类乱拨.如果您急忙,请随意浏览摘要"小节.)

UK g . HK g m . FK g将是Hask endofunctor,与Three构造相对应.我们进一步希望它成为Hask上的单子.我们可以通过将HK g m设置为Kleisli- g类别中的monad来确保这一点.这意味着我们需要找出Kleisli- gfmapreturnjoin的对应项:

UK g . HK g m . FK g will be a Hask endofunctor, the counterpart to Three construction. We further want it to be a monad on Hask. We can ensure that by setting up HK g m as a monad on the Kleisli-g category. That means we need to figure out counterparts to fmap, return and join on Kleisli-g:

kmap : {a} ~> {b} |-> {m a} ~> {m b}
       (a -> g b)  ->  m a -> g (m b)

kreturn : {a} ~> {m a}
          a -> g (m a)

kjoin : {m (m a)} ~> {m a}
        m (m a) -> g (m a) 

对于kreturnkjoin,让我们尝试可能会起作用的最简单的方法:

For kreturn and kjoin, let's try the simplest things that could possibly work:

kreturn :: (Monad g, Monad m) => a -> g (m a)
kreturn = return . return 

kjoin :: (Monad g, Monad m) => m (m a) -> g (m a)
kjoin = return . join

kmap有点棘手. fmap @m会给出m (g a)而不是g (m a),因此我们需要一种将g层拉到外面的方法.碰巧,有一种简便的方法可以做到这一点,但是它要求g一个Distributive函子:

kmap is somewhat trickier. fmap @m will give out m (g a) instead of g (m a), so we'd need a way to pull the g layer outside. As it happens, there is a convenient way to do that, but it requires g to be a Distributive functor:

kmap :: (Monad g, Distributive g, Monad m) => (a -> g b)  ->  m a -> g (m b)
kmap f = distribute . fmap f  -- kmap = collect

法律和分配条件

这些猜测当然毫无意义,除非我们可以证明它们是合法的:

Laws and distributivity conditions

Those guesses, of course, mean nothing unless we can show they are lawful:

-- Functor laws for kmap
kmap return = return
kmap g <=< kmap f = kmap (g <=< f)

-- Naturality of kreturn
kmap f <=< kreturn = kreturn <=< f

-- Naturality of kjoin
kjoin <=< kmap (kmap f) = kmap f <=< kjoin

-- Monad laws
kjoin <=< kreturn = return
kjoin <=< kmap kreturn = return
kjoin <=< kmap kjoin = kjoin <=< kjoin

解决这个问题表明组成具有分配律的单子的四个条件足以确保法律成立:

Working it out shows the four conditions for composing monads with a distributive law are sufficient to ensure the laws hold:

-- dist :: t (g a) -> g (t a)
-- I'm using `dist` instead of `distribute` and `t` instead of `m` here for the
-- sake of notation neutrality. 
dist . fmap (return @g) = return @g                 -- #1
dist . return @t = fmap (return @t)                 -- #2
dist . fmap (join @g) = join @g . fmap dist . dist  -- #3
dist . join @t = fmap (join @t) . dist . fmap dist  -- #4
-- In a nutshell: dist must preserve join and return for both monads.

在我们的案例中,条件#1给出了kmap身份,kjoin右身份和kjoin关联性; #2赋予kreturn的自然性; #3,函子组成; #4,kjoin的自然性(kjoin左身份不依赖于四个条件中的任何一个).最终的健全性检查正在确定条件本身要满足的条件.在distribute的特定情况下,其非常强的自然属性意味着任何合法的Distributive都必须具备四个条件,所以我们很高兴.

In our case, condition #1 gives kmap identity, kjoin right identity and kjoin associativity; #2 gives kreturn naturality; #3, functor composition; #4, kjoin naturality (kjoin left identity doesn't depend on any of the four conditions). The final sanity check is establishing what it takes for the conditions themselves to hold. In the specific case of distribute, its very strong naturality properties mean the four conditions necessarily hold for any lawful Distributive, so we're good to go.

整个UK g . HK g m . FK g monad可以通过将HK g m拆分为Kleisli附加词而从我们已有的片段中获得,这与我们开始时使用的Kleisli附加词完全相似,只是我们从Klesili -g开始比Hask:

The whole UK g . HK g m . FK g monad can be derived from the pieces we already have by splitting HK g m into a Kleisli adjunction, which is entirely analogous to the Kleisli adjunction we began with, except we start from Klesili-g rather than Hask:

HK g m = UHK g m . FHK g m

FHK g m : {a}        |-> <{a}>
      f : {a} ~> {b} |-> fmap return . f : <{a}> ~> <{b}> ~ a -> g (m b)
-- kreturn <=< f = fmap (return @m) . f
-- Note that m goes on the inside, so that we end up with a morphism in Kleisli g.

UHK g m : <{a}>          |-> {m a}
      f : <{a}> ~> <{b}> |-> fmap join . distribute . fmap f : {m a} ~> {m b} ~ m a -> g (m b)
-- kjoin <=< kmap f = fmap (join @m) . distribute . fmap f

-- The adjunction isomorphism:
hkla : (FHK g m {a} ~> <{b}>) -> ({a} ~> UHK g m <{b}>)
hkra : ({a} ~> UHK g m <{b}>) -> (FHK g m {a} ~> <{b}>)
-- Just like before, we have:
hkla = id
hkra = id

-- And, for the sake of completeness, a Kleisli composition operator:
-- g <~< f = kjoin <=< kmap g <=< f
(<~<) :: (Monad g, Distributive g, Monad m)
    => (b -> g (m c)) -> (a -> g (m b)) -> (a -> g (m c))
g <~< f = fmap join . join . fmap (distribute . fmap g) . f

现在我们手头有两个附加,我们可以将它们组合起来,形成变压器附加,最后将变压器附加到returnjoin:

Now that we have two adjunctions at hand, we can compose them, leading to the transformer adjunction and, at long last, to return and join for the transformer:

-- The composition of the three morphism mappings in UK g . HK g m . FK g
-- tkmap f = join . fmap (kjoin <=< kmap (kreturn <=< return . f))
tkmap :: (Monad g, Distributive g, Monad m) => (a -> b) -> g (m a) -> g (m b)
tkmap = fmap . fmap

-- Composition of two adjunction units, suitably lifted through the functors.
-- tkreturn = join . fmap (hkla hkid) . kla kid = join . fmap kreturn . return
tkreturn :: (Monad g, Monad m) => a -> g (m a)
tkreturn = return . return

-- Composition of the adjunction counits, suitably lifted through the functors.
-- tkjoin = join . fmap (kjoin <=< kmap (hkra kid <~< (kreturn <=< kra id)))
--    = join . fmap (kjoin <=< kmap (return <~< (kreturn <=< id)))
tkjoin :: (Monad g, Distributive g, Monad m) => g (m (g (m a))) -> g (m a)
tkjoin = fmap join . join . fmap distribute

对于liftkmap (return @g)听起来像是一个明智的定义.这相当于distribute . fmap return(与 Benjamin Hodgson对Simon C问题的回答中的lift相比) #1变得简单:

As for lift, kmap (return @g) sounds like a sensible definition. That amounts to distribute . fmap return (compare with the lift from Benjamin Hodgson's answer to Simon C's question), which by condition #1 becomes simply:

tklift :: m a -> g (m a)
tklift = return

MonadLift定律(意味着tklift必须是monad态)确实成立,join定律取决于分配条件#1:

The MonadLift laws, which mean tklift must be a monad morphism, do hold, with the join law hinging on distributivity condition #1:

tklift . return = tkreturn
tklift . join = tkjoin . tkmap tklift . tklift

总结

Kleisli附加条件使我们可以通过将任何Distributive单核分子构建在任何其他单核分子的外部来构建反型异构体.放在一起,我们有:

In summary

The Kleisli adjunction allows us to construct a transfomer from any Distributive monad by composing it on the outside of any other monad. Putting it all together, we have:

-- This is still a Three, even though we only see two Hask endofunctors.
-- Or should we call it FourK?
newtype ThreeK g m a = ThreeK { runThreeK :: g (m a) }

instance (Functor g, Functor m) => Functor (ThreeK g m) where
    fmap f (ThreeK m) = ThreeK $ fmap (fmap f) m

instance (Monad g, Distributive g, Monad m) => Monad (ThreeK g m) where
    return a = ThreeK $ return (return a)
    m >>= f = ThreeK $ fmap join . join . fmap distribute 
        $ runThreeK $ fmap (runThreeK . f) m

instance (Monad g, Distributive g, Monad m) => Applicative (ThreeK g m) where
    pure = return
    (<*>) = ap

instance (Monad g, Distributive g) => MonadTrans (ThreeK g) where
    lift = ThreeK . return

Distributive的典型示例是函数functor.将其放在另一个monad的外面给出ReaderT:

The quintessential example of Distributive is the function functor. Composing it on the outside of another monad gives out ReaderT:

newtype KReaderT r m a = KReaderT { runKReaderT :: r -> m a }
    deriving (Functor, Applicative, Monad) via ThreeK ((->) r) m
    deriving MonadTrans via ThreeK ((->) r)

ThreeK实例与典型的ReaderT实例完全一致.

The ThreeK instances perfectly agree with the canonical ReaderT ones.

在上面的推导中,我们将基本monad Klesli附加语堆叠在特征monad附加语之上.可以想象,我们可以从相反的基本monad附加词开始做相反的事情.定义kmap时,将会发生至关重要的变化.由于基本monad原则上可以是任何monad,因此我们不想对它施加Distributive约束,以便可以将其向外拉,就像在上面的派生中对g所做的那样.更好地适合我们的游戏计划的是,双重要求功能monad中的Traversable,以便可以使用sequenceA将其推入内部.这将导致一个在内部而不是外部构成特征单子的变压器.

In the derivation above, we stacked the base monad Klesli adjunction atop the feature monad adjunction. We could conceivably do it the other way around, and start from the base monad adjunction. The crucial change that would happen would come about when defining kmap. As the base monad can, in principle, be any monad, we wouldn't want to slap a Distributive constraint on it so that it can be pulled outwards, like we did with g in the derivation above. A better fit for our game plan would be, dually, requiring Traversable from the feature monad, so that it can be pushed inside with sequenceA. This will lead to a transformer that composes the feture monad on the inside, rather than on the outside.

下面是整体内部结构.我之所以称它为ThreeEM,是因为它也可以通过使用Eilenberg-Moore附加语(而不是Kleisli附加语)并将它们与基础monad堆叠在一起而获得,就像Simon C的Three一样.这个事实可能与Eilenberg-Moore附加物和Klesili附加物之间的对偶有关.

Below is the overall feature-on-the-inside construction. I called it ThreeEM because it can also be obtained by using Eilenberg-Moore adjunctions (instead of Kleisli ones) and stacking them with the base monad on the top, as in Simon C's Three. This fact probably has to do with the duality between the Eilenberg-Moore and Klesili adjunctions.

newtype ThreeEM t m a = ThreeEM { runThreeEM :: m (t a) }

instance (Functor t, Functor m) => Functor (ThreeEM t m) where
    fmap f (ThreeEM m) = ThreeEM $ fmap (fmap f) m

instance (Monad t, Traversable t, Monad m) => Monad (ThreeEM t m) where
    return a = ThreeEM $ return (return a)
    m >>= f = ThreeEM $ fmap join . join . fmap sequenceA 
      $ runThreeEM $ fmap (runThreeEM . f) m

instance (Monad t, Traversable t, Monad m) => Applicative (ThreeEM t m) where
    pure = return
    (<*>) = ap

-- In terms of of the Kleisli construction: as the bottom adjunction is now the
-- base monad one, we can use plain old fmap @m instead of kmap to promote return. 
instance (Monad t, Traversable t) => MonadTrans (ThreeEM t) where
    lift = ThreeEM . fmap return

以这种方式出现的普通变压器包括MaybeTExceptT.

Common transformers that arise in this fashion include MaybeT and ExceptT.

这种构造有一个潜在的陷阱. sequenceA必须遵守分配条件,以便实例合法.但是,其Applicative约束使其自然属性比distribute弱得多,因此条件并非全部免费:

There is one potential pitfall with this construction. sequenceA has to follow the distributivity conditions so that the instances are lawful. Its Applicative constraint, however, makes its naturality propertes a lot weaker than those of distribute, and so the conditions don't all hold for free:

  • 条件#1确实成立:这是

  • Condition #1 does hold: it is a consequence of the identity and naturality laws of Traversable.

条件#2也成立:sequenceA保留可遍历函子上的自然转换,只要这些转换保留toList.如果我们认为returnIdentity的自然转变,那将立即成立案例.

Condition #2 also holds: sequenceA preserves natural transformations on the traversable functor as long as those transformations preserve toList. If we regard return as a natural transformation from Identity, that immediately holds case.

条件#3.如果将join @m作为对Compose m m的自然转换而保留了(<*>),则将保持不变,但事实并非如此.如果sequenceA实际上对效果进行排序(即,如果可遍历可以保存一个以上的值),则在基本monad中执行join(<*>)的顺序引起的任何差异都将导致违反条件.顺便说一句,这是臭名昭著的"ListT做错了"问题的一部分:按照这种构造构建的变压器中的ListT仅在与可交换基本单子一起使用时才合法.

Condition #3, however, is not guaranteed. It would hold if join @m, taken as a natural transformation from Compose m m, preserved (<*>), but that might not be the case. If sequenceA actually sequences effects (that is, if the traversable can hold more than one value), any differences arising from the order in which join and (<*>) are performed in the base monad will lead to the condition being violated. That, incidentally, is part of the notorious "ListT done wrong" problem: the ListT in transformers, built in accordance with this construction, is only lawful if used with commutative base monads.

最后,仅当条件join @t(作为对Compose t t的自然转换而来)保留了toList(即,如果它不删除,重复或重新排列元素)时,条件#4才成立.一个结果是,即使我们尝试在纸上覆盖join嵌套结构的对角线的要素monad(也通常是Distributive实例的monad的情况),这种构造也不起作用.条件#3通过限制我们自己使用可交换的基本单子.

Finally, condition #4 only holds if join @t, taken as a natural transformation from Compose t t, preserves toList (that is, if it doesn't drop, duplicate, or rearrange elements). One consequence is that this construction won't work for feature monads whose join "takes the diagonal" of the nested structure (as is generally the case for monads that also are Distributive instances), even if we try to paper over condition #3 by restricting ourselves to commutative base monads.

这些限制意味着该结构不能像人们希望的那样广泛地应用.最终,Traversable约束太宽泛了.我们真正需要的可能是将monad功能作为仿射可遍历的对象(即,一个容器最多容纳一个元素-请参阅

Those restrictions mean the construction isn't quite as widely applicable as one might like. Ultimately, the Traversable constraint is too broad. What we really need is probably to have the feature monad as an affine traversable (that is, a container that holds at most one element -- see this post by Oleg Grenrus for some lens-flavoured discussion); as far as I'm aware of, there is no canonical Haskell class for that, though.

到目前为止,所描述的构造要求特征monad分别为DistributiveTraversable.但是,总体策略并非完全针对Kleisli和Eilenberg-Moore附加语,因此可以想象在使用其他附加语时对其进行尝试.即使State既不是Distributive也不是Traversable,该临时附属语通过Simon C的Three/AdjointT导致StateT的事实可能表明这样的尝试可能是富有成果的.但是,我对此并不乐观.

The constructions described thus far require the feature monad to be Distributive or Traversable, respectively. The overarching strategy, though, is not at all specific to the Kleisli and Eilenberg-Moore adjunctions, so it is conceivable to attempt it while using other adjunctions. The fact that the currying adjunction leads to StateT via Simon C's Three/AdjointT, even though State is neither Distributive nor Traversable, might suggest such attempts could be fruitful. I'm not optimistic about that, however.

在其他地方的相关讨论中,本杰明·霍奇森(Benjamin Hodgson)猜想,所有诱导单声道的附加指令都导致同一变压器.考虑到所有这些附加语都通过唯一的函子与Kleisli和Eilenberg-Moore附加语相关联,这听起来很合理(关于这一点,请参见上下文中的类别理论" ,命题5.2.12).恰当的例子:如果我们尝试使用ThreeK构造使用List,但是使用对单物类的免费/健忘的附加词而不是Kleisli- [],我们最终会使用m []变压器ThreeEM/内部特征构造将使我们到需要join成为应用同态的"ListT做错问题".

In a related discussion elsewhere, Benjamin Hodgson conjectures that all adjunctions inducing a monad lead to the same transformer. That sounds very plausible, considering that all such adjunctions are related through unique functors to both the Kleisli and the Eilenberg-Moore adjunctions (on that, see Category Theory in Context, proposition 5.2.12). Case in point: if we attempt List with the ThreeK construction but using the free/forgetful adjunction to the category of monoids instead of Kleisli-[], we end up with the m [] transformer the ThreeEM/feature-on-the-inside construction would give us, down to the "ListT done wrong problem" of needing join to be an applicative homomorphism.

那么State及其产生变压器的第三附加物呢?尽管我尚未详细说明,但我认为更恰当的做法是考虑将distributesequenceA(分别用于此处的构造)分别属于右侧和左侧伴随对象,而不是整个功能单子.在distribute的情况下,这将超出Haskell类型签名的范围...

What about State and its transformer-producing third adjunction, then? Though I haven't worked it out in detail, I suspect it is more appropriated to think of distribute and sequenceA, as used in the constructions here, as belonging to the right and left adjoints, respectively, rather than to the whole feature monad. In the case of distribute, that would amount to looking beyond the Haskell type signature...

distribute :: (Distribute g, Functor m) => m (g a) -> g (m a)

...了解Kleisli- g -Hask仿函数之间的自然转换:

... to see a natural transformation between Kleisli-g-to-Hask functors:

distribute  : m . UK g |-> UK g . HK g m

如果我对此表示正确,则可以反过来回答此问题,并根据特征monad的Kleisli附加词重新解释Three/AdjointT构造.如果真是这样,State根本不会告诉我们有关既不是Distributive也不是Traversable的其他功能单子的信息.

If I am right about that, it will be possible to turn this answer around and reinterpret the Three/AdjointT construction in terms of the Kleisli adjunction of the feature monad. If that is the case, State doesn't tell us much at all about other feature monads that are neither Distributive nor Traversable.

还值得注意的是,并非所有的变压器都是通过按这里所见的方式通过附加成分的组合来混合单声道效果而产生的.在 transformers 中,ContT和[SelectT)不遵循该模式;但是,我要说的是,它们太古怪,无法在这种情况下进行讨论(不是单子类的函子", "ListT正确完成" 实现,它通过以不需要在绑定中对它们进行排序的方式将基本monad效果啮合在一起,从而避免了与sequenceA相关的非法问题(以及流问题的丢失).的变压器.这是一个实现的草图,出于说明目的:

It is also worth noting that not all transformers come about from blending monadic effects through the composition of adjunctions in the way have seen here. In transformers, ContT and [SelectT do not follow the pattern; however, I'd say they are too wacky to be discussed in this context ("not a functor on the category of monads", as the docs point out). A better example is provided by the various "ListT done right" implementations, which avoid the unlawfulness problems associated with sequenceA (as well as the loss of streaming problems) by enmeshing the base monad effects in a way that doesn't require sequencing them in the bind of the transformer. Here is a sketch of an implementation, for illustrative purposes:

-- A recursion-schemes style base functor for lists.
data ListF a b = Nil | Cons a b
    deriving (Eq, Ord, Show, Functor)

-- A list type might be recovered by recursively filling the functorial
-- position in ListF.
newtype DemoList a = DemoList { getDemoList :: ListF a (DemoList a) }

-- To get the transformer, we compose the base monad on the outside of ListF.
newtype ListT m a = ListT { runListT :: m (ListF a (ListT m a)) }
    deriving (Functor, Applicative, Alternative) via WrappedMonad (ListT m)

-- Appending through the monadic layers. Note that mplus only runs the effect
-- of the first ListF layer; everything eslse can be consumed lazily.
instance Monad m => MonadPlus (ListT m) where
    mzero = ListT $ return Nil
    u `mplus` v = ListT $ runListT u >>= \case
        Nil -> runListT v
        Cons a u' -> return (Cons a (u' `mplus` v))

-- The effects are kept apart, and can be consumed as they are needed.
instance Monad m => Monad (ListT m) where
    return a = ListT $ pure (Cons a mzero)
    u >>= f = ListT $ runListT u >>= \case
        Nil -> return Nil
        Cons a v -> runListT $ f a `mplus` (v >>= f)

instance MonadTrans ListT where
    lift m = ListT $ (\a -> Cons a mzero) <$> m

在此ListT中,基本单声道效果既不在列表的内部也不在列表的外部.相反,它们用螺栓固定在列表的书脊上,通过根据ListF定义类型将其变得有形.

In this ListT, the base monad effects are neither on the inside nor on the outside of the list. Rather, they are bolted on the spine of the list, made tangible by defining the type in terms of ListF.

以类似方式构建的相关变压器包括free-monad变压器管道文档).

Related transformers that are built in a similar way include the free-monad transformer FreeT, as well as the core monad transformers from effectful streaming libraries (it is no coincidence that the "ListT done right" link I included above points to the pipes documentation).

这种变压器可以某种方式与此处描述的附加堆叠策略相关吗?我还没有足够努力地解决这个问题.这似乎是一个值得思考的有趣问题.

Can this kind of transformer be somehow related to the adjunction stacking strategy describe here? I haven't looked sufficiently hard at the matter to tell; it looks like an interesting question to ponder about.

这篇关于通常说来,monad变压器是否由附加条件引起的?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆