18

由于_+_-Operation forNat通常是在第一个参数中递归定义的,因此类型检查器知道它显然不是微不足道的i + 0 == i。但是,当我在固定大小的向量上编写函数时,我经常遇到这个问题。

一个例子:我如何定义一个 Agda 函数

swap : {A : Set}{m n : Nat} -> Vec A (n + m) -> Vec A (m + n)

哪个将第一个n值放在向量的末尾?

由于 Haskell 中的一个简单解决方案是

swap 0 xs     = xs
swap n (x:xs) = swap (n-1) (xs ++ [x])

我在 Agda 中类似地尝试过,如下所示:

swap : {A : Set}{m n : Nat} -> Vec A (n + m) -> Vec A (m + n)    
swap {_} {_} {zero} xs          = xs 
swap {_} {_} {suc i} (x :: xs)  = swap {_} {_} {i} (xs ++ (x :: []))

但是类型检查器失败并显示消息(与{zero}上述swap-Definition 中的 -case 相关):

.m != .m + zero of type Nat
when checking that the expression xs has type Vec .A (.m + zero)

所以,我的问题是:如何教 Agda m == m + zero?以及如何swap在 Agda 中编写这样的函数?

4

1 回答 1

15

教 Agdam == m + zero并不太难。例如,使用等式证明的标准类型,我们可以写出这样的证明:

rightIdentity : (n : Nat) -> n + 0 == n
rightIdentity zero = refl
rightIdentity (suc n) = cong suc (rightIdentity n)

rewrite然后我们可以使用关键字告诉 Agda 使用这个证明:

swap : {A : Set} {m n : Nat} -> Vec A (n + m) -> Vec A (m + n)    
swap {_} {m} {zero} xs rewrite rightIdentity m = xs 
swap {_} {_} {suc i} (x :: xs) = ?

然而,为第二个等式提供必要的证明要困难得多。一般来说,尝试使您的计算结构与您的 types 的结构相匹配是一个更好的主意。这样,您就可以少做定理证明(或者在这种情况下没有)。

例如,假设我们有

drop : {A : Set} {m : Nat} -> (n : Nat) -> Vec A (n + m) -> Vec A m
take : {A : Set} {m : Nat} -> (n : Nat) -> Vec A (n + m) -> Vec A n

(这两者都可以在没有任何定理证明的情况下定义),Agda 很乐意接受这个定义,而不用大惊小怪:

swap : {A : Set} {m n : Nat} -> Vec A (n + m) -> Vec A (m + n)
swap {_} {_} {n} xs = drop n xs ++ take n xs
于 2012-10-18T16:00:57.370 回答