14

给定一个(??)这样的操作

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

(也就是说(??)是联想的)

一定是这样吗

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

(也就是说liftA2 (??)是联想的)

如果我们愿意,我们可以将其重写为:

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

我花了一点时间盯着适用的法律,但我无法拿出证据证明情况确实如此。所以我开始反驳它。我尝试过的所有开箱即用的应用程序(Maybe[]Either等)都遵循法律,所以我想我会创建自己的。

我最好的想法是制作一个空的应用程序,并附加一条额外的信息。

data Vacuous a = Vac Alg

Alg稍后我会根据自己的方便定义一些代数在哪里,以使财产失败但适用的法律成功。

现在我们这样定义我们的实例:

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)

哪里iAlg要确定的某个元素,并且comb是一个二元组合子Alg也有待确定。我们实际上没有其他方法可以定义这一点。

如果我们想实现同一性法则,这将迫使i同一性结束comb。然后我们免费获得同态交换。但是现在组合力强制comb关联Alg

((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)

强迫我们满足财产。

有反例吗?如果不是,我们如何证明这个属性?

4

2 回答 2

5

我们首先使用应用定律重写左侧。回想一下<$><*>都是左结合的,所以我们有例如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

最后一种形式表明,从本质上讲,原始表达式“运行”动作ab、 ,并c以这种方式对它们的效果进行排序,然后用于(??)纯粹组合三个结果。

然后我们可以证明右手边等价于上面的形式。

(??) <$> 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

现在,我们只需要以((.) ($ (??)) ((.) (.) ((.) (.) (??))))更易读的有点形式重写无点项,这样我们就可以使它等于我们在证明的前半部分得到的项。这只是申请(.)($)需要的问题。

((.) ($ (??)) ((.) (.) ((.) (.) (??))))
= \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

在最后一步中,我们利用了 的关联性(??)

(哇。)

于 2020-05-27T07:55:06.837 回答
4

它不仅保留了关联性,我想说这可能是应用法则背后的主要思想!

回想一下该类的数学形式:

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

有法律的

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

在这种方法中,liftA2将元组值函数映射到已经准备好的压缩对上的因素:

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

IE

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

现在说我们给了

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

或无点(再次忽略元组括号交换)

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

如果我们以这种风格编写所有内容,很容易看出关联性保留基本上是 just 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))
于 2020-05-27T12:41:16.097 回答