我对描述here的构造感兴趣,因为它确定了来自伴随函子的monad变换器。这里有一些代码总结了基本概念:
{-# LANGUAGE MultiParamTypeClasses #-}
import Control.Monad
newtype Three g f m a = Three { getThree :: g (m (f a)) }
class (Functor f, Functor g) => Adjoint f g where
counit :: f (g a) -> a
unit :: a -> g (f a)
instance (Adjoint f g, Monad m) => Monad (Three g f m) where
return = Three . fmap return . unit
m >>= f = Three $ fmap (>>= counit . fmap (getThree . f)) (getThree m)
instance (Adjoint f g, Monad m) => Applicative (Three g f m) where
pure = return
(<*>) = ap
instance (Adjoint f g, Monad m) => Functor (Three g f m) where
fmap = (<*>) . pure
鉴于Adjoint ((,) s) ((->) s)
,Three ((->) s) ((,) s)
似乎等同于StateT s
。
非常酷,但我对一些事情感到困惑:
如何将monadic m a
升级为monadic Three g f m a
?对于Three ((->) s) ((,) s)
的特定情况,它当然很明显如何执行此操作,但似乎需要一个适用于Three g f
Adjoint f g
的任何lift
的配方。换句话说,似乎应该有unit
的模拟,其定义只需要counit
,return
以及输入的>>=
和f
单子。但我似乎找不到一个(我看过a definition using sequence
,但这似乎有点像作弊,因为它需要Traversable
为g a
。
就此而言,我们如何将Three g f m a
升级为Adjoint f g
(提供Three ((->) s) ((,) s)
)?同样,对于gets
的特定情况,显而易见的是如何做到这一点,但我想知道是否有unit
的模拟只需要{{1} }},counit
以及输入monad的return
和>>=
。
答案 0 :(得分:3)
我们如何将monadic
m a
升级为monadicThree g f m a
?
好问题。打网球的时间!
-- i'm using Adjuction from the adjunctions package because I'll need the fundeps soon
lift :: Adjunction f g => m a -> Three g f m a
lift mx = Three _
洞的类型为g (m (f a))
。我们在范围内mx :: m a
,当然还有unit :: a -> g (f a)
和fmap :: (a -> b) -> m a -> m b
。
lift mx = let mgfx = fmap unit mx
in Three $ _ mgfx
现在它是_ :: m (g (f a)) -> g (m (f a))
。如果g
为distribute
,则为Distributive
。
lift mx = let mgfx = fmap unit mx
gmfx = distributeR mgfx
in Three gmfx
-- or
lift = Three . distributeR . fmap unit
所以现在我们只需要证明一个附属的右边总是Distributive
:
distributeR :: (Functor m, Adjunction f g) => m (g x) -> g (m x)
distributeR mgx = _
由于我们需要返回g
,因此Adjunction
明确选择方法为leftAdjunct :: Adjunction f g => (f a -> b) -> a -> g b
,使用unit
创建g (f a)
,然后通过f a
ping函数来删除内部fmap
。
distributeR mgx = leftAdjunct (\fa -> _) _
我要先攻击第一洞,期望填补它可能会告诉我一些关于第二洞的事情。第一个洞的类型为m a
。我们唯一可以获得任何类型m
的方法是fmap
ping mgx
以上的内容。
distributeR mgx = leftAdjunct (\fa -> fmap (\gx -> _) mgx) _
现在第一个洞的类型为a
,我们的范围为gx :: g a
。如果我们有f (g a)
,我们可以使用counit
。但我们确实有一个f x
(其中x
目前是一个模糊的类型变量)和范围内的g a
。
distributeR mgx = leftAdjunct (\fa -> fmap (\gx -> counit (fa $> gx)) mgx) _
事实证明,剩下的洞有一个模糊的类型,所以我们可以使用我们想要的任何东西。 ($>
将忽略它。)
distributeR mgx = leftAdjunct (\fa -> fmap (\gx -> counit (fa $> gx)) mgx) ()
这种衍生可能看起来像一个魔术,但实际上你通过练习在打网球方面变得更好。游戏的技巧是能够查看类型并应用有关您正在使用的对象的直觉和事实。从查看类型我可以告诉我需要交换m
和g
,并且遍历m
不是一个选项(因为m
不一定是{ {1}}),所以Traversable
之类的东西是必要的。
除了猜测我需要实施distribute
之外,我还有一些关于结合如何工作的一般知识的指导。
具体而言,当您谈论distribute
时,唯一有趣的附加内容(唯一同构)* -> *
/ Reader
附加内容。特别是,这意味着Writer
上的任何正确伴随始终是Representable
,由tabulateAdjunction
和indexAdjunction
见证。我也知道所有Hask
个仿函数都是Representable
(实际上逻辑上反过来也是如此,如Distributive
's docs所述,即使这些类的功效不相同),每distributeRep
。
就此而言,我们如何将
Distributive
升级为g a
(提供Three g f m a
)?
我将此作为练习。我怀疑你需要再次依靠Adjoint f g
同构。我实际上并不希望这一个对所有附加都是正确的,只有g ~ ((->) s)
上的那些,其中只有一个。
答案 1 :(得分:2)
lift
设置为:
lift mx = let mgfx = fmap unit mx gmfx = distributeR mgfx in Three gmfx -- or lift = Three . distributeR . fmap unit
如您所知,这不是我们可能在那里使用的唯一合理策略:
lift mx = let gfmx = unit mx
gmfx = fmap sequenceL gfmx
in Three gmfx
-- or
lift = Three . fmap sequenceL . unit
Traversable
对Edward Kmett's corresponding MonadTrans
instance的要求始于何时。那么,问题就在于,依赖于那个,就像你说的那样,&#34;作弊&#34;。我认为它不是。
我们可以调整本杰明关于Distributive
和右边界的游戏计划,并尝试查找左边邻居是否为Traversable
。查看Data.Functor.Adjunction
表明我们有一个非常好的工具箱可供使用:
unabsurdL :: Adjunction f u => f Void -> Void
cozipL :: Adjunction f u => f (Either a b) -> Either (f a) (f b)
splitL :: Adjunction f u => f a -> (a, f ())
unsplitL :: Functor f => a -> f () -> f a
爱德华有用地告诉我们unabsurdL
和cozipL
见证&#34; [a]左伴随必须有人居住,[并且]左伴随必须由一个元素居住&#34 ;, 分别。但是,这意味着splitL
精确对应于表征Traversable
仿函数的形状和内容分解。如果我们添加splitL
和unsplitL
是倒置的事实,则会立即执行sequence
:
sequenceL :: (Adjunction f u, Functor m) => f (m a) -> m (f a)
sequenceL = (\(mx, fu) -> fmap (\x -> unsplitL x fu) mx) . splitL
(请注意,Functor
要求m
不超过lift
,正如对于只包含一个值的可遍历容器所预期的那样。)
此时缺少的是验证distributeR
的两个实现都是等效的。这并不困难,只是有点费力。简而言之,此处的sequenceR
和distributeR = \mgx ->
leftAdjunct (\fa -> fmap (\gx -> rightAdjunct (const gx) fa) mgx) ()
sequenceL =
rightAdjunct (\mx -> leftAdjunct (\fu -> fmap (\x -> fmap (const x) fu) mx) ())
定义可以简化为:
distributeR . fmap unit = fmap sequenceL . unit
我们希望显示distributeR . fmap unit = \mgx ->
leftAdjunct (\fa -> fmap (\gx -> rightAdjunct (const (unit gx)) fa) mgx) ()
fmap sequenceL . unit = \mx ->
leftAdjunct (\fu -> fmap (\x -> fmap (const x) fu) mx) ()
。经过几轮简化后,我们得到:
\fu -> fmap (\x -> fmap (const x) fu) mx
我们可以通过选择leftAdjunct
- 第二个右侧rightAdjunct unit = counit . fmap unit = id
的参数 - 并将\fu -> fmap (\x -> fmap (const x) fu) mx
\fu -> fmap (\x -> fmap (const x) fu) mx
\fu -> fmap (\x -> (counit . fmap unit . fmap (const x)) fu) mx
\fu -> fmap (\x -> rightAdjunct (unit . const x) fu) mx
\fu -> fmap (\x -> rightAdjunct (const (unit x)) fu) mx
-- Sans variable renaming, the same as
-- \fa -> fmap (\gx -> rightAdjunct (const (unit gx)) fa) mgx
滑入其中来显示这些内容真的相同:
Traversable
需要注意的是,MonadTrans
路径Distributive
与Control.Monad.Trans.Adjoint
路径一样安全,并且对此感到担忧 - 包括lift
提到的路线文档 - 不再麻烦任何人。
P.S。:值得注意的是,这里提出的lift = Three . leftAdjunct sequenceL
的定义可以拼写为:
lift
也就是说,sequenceL
是通过附录同构发送的leftAdjunct sequenceL = distributeR . fmap unit
。另外,来自......
rightAdjunct
...如果我们双方都申请sequenceL = rightAdjunct (distributeR . fmap unit)
,我们就会......
fmap (fmap counit)
......如果我们在双方左侧撰写distributeR = leftAdjunct (fmap counit . sequenceL)
,我们最终会得到:
distributeR
因此sequenceL
和vector<vector<Edge> >graph
是可以相互确定的。