可能适用的证明成分法

时间:2014-06-09 21:15:51

标签: haskell applicative

所以,我想手动证明Maybe applicative的组成法则是:

u <*> (v <*> w) = pure (.) <*> u <*> v <*> w

我用这些步骤证明了这一点:

u <*> (v <*> w)          [Left hand side of the law]
  = (Just f) <*> (v <*> w)  [Assume u ~ Just f]
  = fmap f (v <*> w)
  = fmap f (Just g <*> w)   [Assume v ~ Just g]
  = fmap f (fmap g w)
  = fmap (f . g) w

pure (.) <*> u <*> v <*> w  [Right hand side of the law]
  = Just (.) <*> u <*> v <*> w
  = fmap (.) u <*> v <*> w
  = fmap (.) (Just f) <*> v <*> w  [Replacing u with Just f]
  = Just (f .) <*> v <*> w
  = Just (f .) <*> Just g <*> w    [Replacing v with Just g]
  = fmap (f .) (Just g) <*> w
  = Just (f . g) <*> w
  = fmap (f . g) w

证明这是正确的吗?我真正关心的是我假设uv嵌入Just数据构造函数中的某些函数来继续我的证明。那可以接受吗?有没有更好的方法来证明这一点?

4 个答案:

答案 0 :(得分:9)

Applicative functor表达式只是某些仿函数上下文中的函数应用程序。因此:

pure f <*> pure a <*> pure b <*> pure c

-- is the same as:

pure (f a b c)

我们要证明:

pure (.) <*> u <*> v <*> w == u <*> (v <*> w)

考虑:

u = pure f
v = pure g
w = pure x

因此,左侧是:

pure (.) <*> u <*> v <*> w

pure (.) <*> pure f <*> pure g <*> pure x

pure ((.) f g x)

pure ((f . g) x)

pure (f (g x))

pure f <*> pure (g x)

pure f <*> (pure g <*> pure x)

u <*> (v <*> w)

对于Maybe,我们知道pure = Just。因此,如果uvwJust值,那么我们就知道合成法有效。

但是,如果其中任何一个是Nothing怎么办?我们知道:

Nothing <*> _ = Nothing
_ <*> Nothing = Nothing

因此,如果其中任何一个是Nothing,那么整个表达式变为Nothing(第二种情况除外,如果第一个参数是undefined)并且Nothing == Nothing法律仍然有效。

最后,undefined(a.k.a。bottom)值是多少?我们知道:

(Just f) <*> (Just x) = Just (f x)

因此,以下表达式将使程序暂停:

(Just f) <*> undefined
undefined <*> (Just x)
undefined <*> Nothing

但是,以下表达式将导致Nothing

Nothing <*> undefined

在任何一种情况下,构成法仍然有效。

答案 1 :(得分:3)

Maybe的定义生成的规则是

x :: a
---------------
Just x :: Maybe a

a type
-----------------
Nothing :: Maybe a

一起
a type
------------------
bottom :: a

如果这些是导致Maybe A的唯一规则,那么只要我们详尽无遗,我们就可以在证明中反转它们(从下到上)。这是对Maybe A类型值的案例检查的论证。

您做了两个案例分析,但并非详尽无遗。可能uv实际上是Nothing或者是底部。

答案 2 :(得分:3)

在证明有关Haskell代码的东西时学习的有用工具是Agda: 这是一个简短的证据,说明你想证明什么:

data Maybe (A : Set) : Set where
  Just : (a : A) -> Maybe A
  Nothing : Maybe A

_<*>_ : {A B : Set} -> Maybe (A -> B) -> Maybe A -> Maybe B
Just f <*> Just a = Just (f a)
Just f <*> Nothing = Nothing
Nothing <*> a = Nothing

pure : {A : Set} -> (a : A) -> Maybe A
pure a = Just a

data _≡_ {A : Set} (x : A) : A → Set where
  refl : x ≡ x

_∘_ : {A B C : Set} ->
      (B -> C) -> (A -> B) -> A -> C
_∘_ f g = λ z → f (g z)

maybeAppComp : {A B C : Set} -> (u : Maybe (B -> A)) -> (v : Maybe (C -> B)) -> (w : Maybe C)
            -> (u <*> (v <*> w)) ≡ (((pure _∘_ <*> u) <*> v) <*> w)
maybeAppComp (Just f) (Just g) (Just w) = refl
maybeAppComp (Just f) (Just g) Nothing = refl
maybeAppComp (Just f) Nothing (Just w) = refl
maybeAppComp (Just f) Nothing Nothing = refl
maybeAppComp Nothing (Just g) (Just w) = refl
maybeAppComp Nothing (Just a) Nothing = refl
maybeAppComp Nothing Nothing (Just w) = refl
maybeAppComp Nothing Nothing Nothing = refl

这说明了其他人指出的几点:

  • 您使用的哪些定义对于证明很重要,应该明确。就我而言,我不想使用Agda的库。
  • 案例分析是制作这类证据的关键。
  • 事实上,一旦案例分析完成,证明就变得微不足道了。 Agda compire / proof系统能够为您统一证明。

答案 3 :(得分:2)

您将(<*>)的使用翻译为fmap。其他答案也做了一些模式匹配。

通常你需要打开函数的定义来推理它们,而不仅仅是假设它们的作用。 (您认为(pure f) <*> xfmap f x

相同

例如,(<*>) ap中的Maybe定义为Control.Applicative(或者Monad可以证明ap等同于Monad如果你重新定义它,并且liftM2 id是从liftM2借用的,liftM2 f m1 m2 = do x <- m1 y <- m2 return $ f x y 定义为u <*> (v <*> w) = liftM2 id u (liftM2 id v w) = do u1 <- u v1 <- do v1 <- v w1 <- w return $ id v1 w1 return $ id u1 v1 = do u1 <- u v1 <- do v1 <- v w1 <- w return $ v1 w1 return $ u1 v1 -- associativity law: (see [1]) = do u1 <- u v1 <- v w1 <- w x <- return $ v1 w1 return $ u1 x -- right identity: x' <- return x; f x' == f x = do u1 <- u v1 <- v w1 <- w return $ u1 $ v1 w1 pure (.) <*> u <*> v <*> w = liftM2 id (liftM2 id (liftM2 id (pure (.)) u) v) w = do g <- do f <- do p <- pure (.) u1 <- u return $ id p u1 v1 <- v return $ id f v1 w1 <- w return $ id g w1 = do g <- do f <- do p <- return (.) u1 <- u return $ p u1 v1 <- v return $ f v1 w1 <- w return $ g w1 -- associativity law: = do p <- return (.) u1 <- u f <- return $ p u1 v1 <- v g <- return $ f v1 w1 <- w return $ g w1 -- right identity: x' <- return x; f x' == f x = do u1 <- u v1 <- v w1 <- w return $ ((.) u1 v1) w1 -- (f . g) x == f (g x) = do u1 <- u v1 <- v w1 <- w return $ u1 $ v1 w1 定义如下:

{{1}}

因此,减少左手侧和右手侧以确定它们是等效的:

{{1}}

现在,右手边:

{{1}}

就是这样。

[1] http://www.haskell.org/haskellwiki/Monad_laws