简体   繁体   English

liftA2 是否保留关联性?

[英]Does liftA2 preserve associativity?

Given an operation (??) such that给定一个操作(??)这样

(a ?? b) ?? c = a ?? (b ?? c)

(that is to say (??) is associative) (也就是说(??)是关联的)

must it be the case that一定是这样吗

liftA2 (??) (liftA2 (??) a b) c = liftA2 (??) a (liftA2 (??) b c)

(that is to say that liftA2 (??) is associative) (也就是说liftA2 (??)是关联的)

If we would prefere we can rewrite this as:如果我们愿意,我们可以将其重写为:

fmap (??) (fmap (??) a <*> b) <*> c = fmap (??) a <*> (fmap (??) b <*> c)

I spent a little while staring at the applicative laws but I couldn't come up with a proof that this would be the case.我花了一点时间盯着适用的法律,但我无法拿出证据证明情况确实如此。 So I set out to disprove it.所以我开始反驳它。 All the out-of-the-box applicatives ( Maybe , [] , Either , etc.) that I have tried, follow the law, so I thought I would create my own.我尝试过的所有开箱即用的应用程序( Maybe[]Either等)都遵循法律,所以我想我会创建自己的。

My best idea was to make a vacuous applicative with an extra piece of information attached.我最好的想法是制作一个空的应用程序,并附加一条额外的信息。

data Vacuous a = Vac Alg

Where Alg would be some algebra I would define at my own convenience later as to make the property fail but the applicative laws succeed. Alg将是一些代数,我稍后会在自己方便时定义为使属性失败但应用定律成功。

Now we define our instances as such:现在我们这样定义我们的实例:

instance Functor Vacuous where
  fmap f = id

instance Applicative Vacuous where
  pure x = Vac i
  liftA2 f (Vac a) (Vac b) = Vac (comb a b)
  (Vac a) <*> (Vac b) = Vac (comb a b)

Where i is some element of Alg to be determined and comb is a binary combinator on Alg also to be determined.其中i是待确定的Alg的某个元素, comb是待确定的Alg上的二进制组合子。 There is not really another way we can go about defining this.我们真的没有其他方法可以定义这个。

If we want to fulfill the Identiy law this forces i to be an idenity over comb .如果我们想满足同一性定律,这将迫使i成为comb上的同一性。 We then get Homomorphism and Interchange for free.然后我们免费获得同态交换 But now Composition forces comb to be associative over Alg但是现在, Composition强制combAlg关联

((pure (.) <*> Vac u) <*> Vac v) <*> Vac w = Vac u <*> (Vac v <*> Vac w)
   ((Vac i <*> Vac u) <*> Vac v) <*> Vac w = Vac u <*> (Vac v <*> Vac w)
               (Vac u <*> Vac v) <*> Vac w = Vac u <*> (Vac v <*> Vac w)
                (Vac (comb u v)) <*> Vac w = Vac u <*> (Vac (comb v w))
                   Vac (comb (comb u v) w) = Vac (comb u (comb v w))
                         comb (comb u v) w = comb u (comb v w)

Forcing us to satisfy the property.强迫我们满足财产。

Is there a counter example?有反例吗? If not how can we prove this property?如果不是,我们如何证明这个属性?

We start by rewriting the left hand side, using the applicative laws.我们首先使用应用定律重写左侧。 Recall that both <$> and <*> are left-associative, so that we have, eg, x <*> y <*> z = (x <*> y) <*> z and x <$> y <*> z = (x <$> y) <*> z .回想一下<$><*>都是左结合的,因此我们有,例如x <*> y <*> z = (x <*> y) <*> zx <$> y <*> z = (x <$> y) <*> z

(??) <$> ((??) <$> a <*> b) <*> c
= fmap/pure law
pure (??) <*> (pure (??) <*> a <*> b) <*> c
= composition law
pure (.) <*> pure (??) <*> (pure (??) <*> a) <*> b <*> c
= homomorphism law
pure ((.) (??)) <*> (pure (??) <*> a) <*> b <*> c
= composition law
pure (.) <*> pure ((.) (??)) <*> pure (??) <*> a <*> b <*> c
= homomorphism law
pure ((.) ((.) (??)) (??)) <*> a <*> b <*> c
= definition (.)
pure (\x -> (.) (??) ((??) x)) <*> a <*> b <*> c
= definition (.), eta expansion
pure (\x y z -> (??) ((??) x y) z) <*> a <*> b <*> c
= associativity (??)
pure (\x y z -> x ?? y ?? z) <*> a <*> b <*> c

The last form reveals that, essentially, the original expression "runs" the actions a , b , and c in that order, sequencing their effects in that way, and then uses (??) to purely combine the three results.最后一种形式表明,本质上,原始表达式按顺序“运行”动作abc ,以这种方式对它们的效果进行排序,然后使用(??)纯粹组合三个结果。

We can then prove that the right hand side is equivalent to the above form.然后我们可以证明右手边等价于上面的形式。

(??) <$> a <*> ((??) <$> b <*> c)
= fmap/pure law
pure (??) <*> a <*> (pure (??) <*> b <*> c)
= composition law
pure (.) <*> (pure (??) <*> a) <*> (pure (??) <*> b) <*> c
= composition law
pure (.) <*> pure (.) <*> pure (??) <*> a <*> (pure (??) <*> b) <*> c
= homomorphism law
pure ((.) (.) (??)) <*> a <*> (pure (??) <*> b) <*> c
= composition law
pure (.) <*> (pure ((.) (.) (??)) <*> a) <*> pure (??) <*> b <*> c
= composition law
pure (.) <*> pure (.) <*> pure ((.) (.) (??)) <*> a <*> pure (??) <*> b <*> c
= homomorphism law
pure ((.) (.) ((.) (.) (??))) <*> a <*> pure (??) <*> b <*> c
= interchange law
pure ($ (??)) <*> (pure ((.) (.) ((.) (.) (??))) <*> a) <*> b <*> c
= composition law
pure (.) <*> pure ($ (??)) <*> pure ((.) (.) ((.) (.) (??))) <*> a <*> b <*> c
= homomorphism law
pure ((.) ($ (??)) ((.) (.) ((.) (.) (??)))) <*> a <*> b <*> c

Now, we only have to rewrite the point-free term ((.) ($ (??)) ((.) (.) ((.) (.) (??)))) in a more readable point-ful form, so that we can make it equal to the term we got in the first half of the proof.现在,我们只需将无点术语((.) ($ (??)) ((.) (.) ((.) (.) (??))))改写成更易读的点—— ful 形式,这样我们就可以使它等于我们在证明的前半部分得到的项。 This is just a matter of applying (.) and ($) as needed.这只是根据需要应用(.)($)的问题。

((.) ($ (??)) ((.) (.) ((.) (.) (??))))
= \x -> (.) ($ (??)) ((.) (.) ((.) (.) (??))) x
= \x -> ($ (??)) ((.) (.) ((.) (.) (??)) x)
= \x -> (.) (.) ((.) (.) (??)) x (??)
= \x y -> (.) ((.) (.) (??) x) (??) y
= \x y -> (.) (.) (??) x ((??) y)
= \x y z -> (.) ((??) x) ((??) y) z
= \x y z -> (??) x ((??) y z)
= \x y z -> x ?? y ?? z

where in the last step we exploited the associativity of (??) .在最后一步中,我们利用了(??)的关联性。

(Whew.) (哇。)

Not only does it preserve associativity, I would say that's perhaps the main idea behind the applicative laws in the first place!它不仅保留了关联性,我想说这可能是应用法则背后的主要思想

Recall the maths-style form of the class:回想一下 class 的数学形式:

class Functor f => Monoidal f where
  funit ::    ()     -> f  ()
  fzip :: (f a, f b) -> f (a,b)

with laws有法律的

zAssc:  fzip (fzip (x,y), z) ≅ fzip (x, fzip (y,z))  -- modulo tuple re-bracketing
fComm:  fzip (fmap fx x, fmap fy y) ≡ fmap (fx***fy) (fzip (x,y))
fIdnt:  fmap id ≡ id                    -- ─╮
fCmpo:  fmap f . fmap g ≡ fmap (f . g)  -- ─┴ functor laws

In this approach, liftA2 factors into fmapping a tuple-valued function over an already ready-zipped pair:在这种方法中, liftA2将一个元组值 function 映射到一个已经准备好的压缩对上:

liftZ2 :: ((a,b)->c) -> (f a,f b) -> f c
liftZ2 f = fmap f . fzip

ie IE

liftZ2 f (a,b) = f <$> fzip (a,b)

Now say we have given现在说我们给了

g :: (G,G) -> G
gAssc:  g (g (α,β), γ) ≡ g (α, g (β,γ))

or point-free (again ignoring tuple-bracket interchange)或无点(再次忽略元组括号交换)

gAssc:  g . (g***id) ≅ g . (id***g)

If we write everything in this style, it's easy to see that associativity-preservation is basically just zAssc , with everything about g happening in a separate fmap step:如果我们以这种风格编写所有内容,很容易看出关联性保留基本上只是zAssc ,有关g的所有内容都发生在单独的fmap步骤中:

liftZ2 g (liftZ2 g (a,b), c)
    {-liftA2'-} ≡ g <$> fzip (g <$> fzip (a,b), c)
{-fIdnt,fComm-} ≡ g . (g***id) <$> fzip (fzip (a,b), c) {-gAssc,zAssc-} ≡ g . (id***g) <$> fzip (a, fzip (b,c))
{-fComm,fIdnt-} ≡ g <$> fzip (a, g <$> fzip (b,c))
    {-liftA2'-} ≡ liftZ2 g (a, liftZ2 g (b,c))

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM