所以,我想手动证明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
证明这是正确的吗?我真正关心的是我假设u
和v
嵌入Just
数据构造函数中的某些函数来继续我的证明。那可以接受吗?有没有更好的方法来证明这一点?
答案 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
。因此,如果u
,v
和w
为Just
值,那么我们就知道合成法有效。
但是,如果其中任何一个是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
类型值的案例检查的论证。
您做了两个案例分析,但并非详尽无遗。可能u
或v
实际上是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
这说明了其他人指出的几点:
答案 3 :(得分:2)
您将(<*>)
的使用翻译为fmap
。其他答案也做了一些模式匹配。
通常你需要打开函数的定义来推理它们,而不仅仅是假设它们的作用。 (您认为(pure f) <*> x
与fmap 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}}
就是这样。