我有一个这样的函数:
open import Data.Char
open import Data.Nat
open import Data.Bool
open import Relation.Binary.PropositionalEquality
open Relation.Binary.PropositionalEquality.≡-Reasoning
foo : Char → Char → ℕ
foo c1 c2 with c1 == c2
... | true = 123
... | false = 456
我想证明当我用相同的参数(foo c c
)调用它时,它总是产生123
:
foo-eq⇒123 : ∀ (c : Char) → foo c c ≡ 123
foo-eq⇒123 c =
foo c c ≡⟨ {!!} ⟩
123 ∎
我能够使用refl
来证明一个微不足道的例子:
foo-example : foo 'a' 'a' ≡ 123
foo-example = refl
所以,我认为我可以把refl
放在洞里,因为所有Agda需要做的是减少foo c c
。但是,用refl
替换孔会产生以下错误:
.../Unfold.agda:15,14-18
(foo c c
| Relation.Nullary.Decidable.Core.isYes
(Relation.Nullary.Decidable.Core.map′ Data.Char.Properties.≈⇒≡
Data.Char.Properties.≈-reflexive
(Relation.Nullary.Decidable.Core.map′
(Data.Nat.Properties.≡ᵇ⇒≡ (toℕ c) (toℕ c))
(Data.Nat.Properties.≡⇒≡ᵇ (toℕ c) (toℕ c)) (T? (toℕ c ≡ᵇ toℕ c)))))
!= 123 of type ℕ
when checking that the expression refl has type foo c c ≡ 123
我怀疑问题是Agda不自动明白c == c
是true
c
:
c==c : ∀ (c : Char) → (c == c) ≡ true
c==c c = refl
.../Unfold.agda:23,10-14
Relation.Nullary.Decidable.Core.isYes
(Relation.Nullary.Decidable.Core.map′ Data.Char.Properties.≈⇒≡
Data.Char.Properties.≈-reflexive
(Relation.Nullary.Decidable.Core.map′
(Data.Nat.Properties.≡ᵇ⇒≡ (toℕ c) (toℕ c))
(Data.Nat.Properties.≡⇒≡ᵇ (toℕ c) (toℕ c)) (T? (toℕ c ≡ᵇ toℕ c))))
!= true of type Bool
when checking that the expression refl has type (c == c) ≡ true
那么,证明我的foo-eq⇒123
定理的正确方法是什么?
Agda的内置Char
类型有点奇怪。让我们将其与一个不奇怪的内置类型ℕ
进行对比。ℕ
的相等性检验如下所示:
_≡ᵇ_ : Nat → Nat → Bool
zero ≡ᵇ zero = true
suc n ≡ᵇ suc m = n ≡ᵇ m
_ ≡ᵇ _ = false
注意n ≡ᵇ n
也不会减少到true
。毕竟,Agda不知道n
是zero
还是suc
,也就是说,这两个条款应该申请哪一个的减值。因此,这与您的Char
示例有相同的问题。但对于ℕ
来说,有一个简单的方法。
让我们再看看你的例子,让我们也添加一个基于ℕ
版本的foo
,bar
。
open import Data.Char using (Char ; _==_ ; _≟_)
open import Data.Nat using (ℕ ; zero ; suc ; _≡ᵇ_)
open import Data.Bool using (true ; false)
open import Relation.Binary.PropositionalEquality using (_≡_ ; refl)
open import Relation.Nullary using (yes ; no)
open import Relation.Nullary.Negation using (contradiction)
foo : Char → Char → ℕ
foo c1 c2 with c1 == c2
... | true = 123
... | false = 456
bar : ℕ → ℕ → ℕ
bar n1 n2 with n1 ≡ᵇ n2
... | true = 123
... | false = 456
那么,ℕ
的简单出路是什么?我们在n
上进行模式匹配/案例分割,以减少n ≡ᵇ n
刚好足够来做我们的证明。即,要么到基本情况zero
,要么到下一个递归步骤suc n
。
bar-eq⇒123 : ∀ n → bar n n ≡ 123
bar-eq⇒123 zero = refl
bar-eq⇒123 (suc n) = bar-eq⇒123 n
ℕ
只有两个构造函数,我们知道≡ᵇ
是什么样子,所以模式匹配是直接的。
对于Char
,事情要复杂得多。长话短说,Char
的相等性测试是根据Agda没有给我们定义的函数toℕ
来定义的。此外,Char
数据类型是假定的,没有任何构造函数。所以像bar-eq⇒123
这样的证明不是Char
的选择。我们没有子句,也没有构造函数。(我不会把'a'
称为Char
的构造函数。与1234
不是ℕ
的构造函数类似)
那么,我们能做什么呢?请注意,您引用的错误消息中的c == c
简化为涉及isYes
的相当复杂的东西。如果我们减少c == c
只有一点点(而不是尽可能多),我们会得到isYes (c ≟ c)
(而不是复杂的错误信息)。
_≟_
是Char
的可判定等式(即,一个"相等与否"的组合)。布尔值和证明)。isYes
去掉证明,给出布尔值。
我对证明的想法是不在c
上做案例分割(就像我们在ℕ
上做的那样),而是在c ≟ c
上。这将产生两种情况,并将isYes
分别减少到true
或false
。true
案例是显而易见的。false
案例可以与可决等式的证明相矛盾。
foo-eq⇒123 : ∀ c → foo c c ≡ 123
foo-eq⇒123 c with c ≟ c
... | yes p = refl
... | no ¬p = contradiction refl ¬p
请注意,反过来,这个证明不容易转化为ℕ
,因为_≡ᵇ_
不是基于可决定的等式和isYes
。
也许一个更好的主意是始终坚持可决定的平等,对于Char
和ℕ
,而不是使用_==_
或_≡ᵇ_
。然后foo
看起来如下:
baz : Char → Char → ℕ
baz c1 c2 with c1 ≟ c2
... | yes p = 123
... | no ¬p = 456
并且foo-eq⇒123
证明将不变地适用于它。