我通过学习Agda来教授自己的依赖类型。
这是一个二进制树的类型,它们的大小是平衡的。
open import Data.Nat
open import Data.Nat.Properties.Simple
data T (A : Set) : ℕ -> Set where
empty : T A 0
leaf : A -> T A 1
bal : ∀ {n} -> T A n -> T A n -> T A (n + n)
heavyL : ∀ {n} -> T A (suc n) -> T A n -> T A (suc (n + n))
树可以完全平衡(bal
),或者左子树可以包含比右子树(heavyL
)多一个元素。 (在这种情况下,下一个插入将进入正确的子树。)这个想法是插入将在树的左半部分和右半部分之间翻转,有效地(确定地)改变输入列表。
我无法定义insert
类型检查:
insert : ∀ {A n} -> A -> T A n -> T A (suc n)
insert x empty = leaf x
insert x (leaf y) = bal (leaf x) (leaf y)
insert x (bal l r) = heavyL (insert x l) r
insert x (heavyL l r) = bal l (insert x r)
Agda拒绝bal l (insert x r)
作为heavyL
案件的右侧:
.n + suc .n != suc (.n + .n) of type ℕ
when checking that the expression bal l (insert x r) has type
T .A (suc (suc (.n + .n)))
我试图用证明修补我的定义......
insert x (heavyL {n} l r) rewrite +-suc n n = bal l (insert x r)
...但我得到了同样的错误消息。 (我误解了rewrite
的作用吗?)
我也尝试在相同的证明假设下转换相同大小的树:
convertT : ∀ {n m A} -> T A (n + suc m) -> T A (suc (n + m))
convertT {n} {m} t rewrite +-suc n m = t
insert x (heavyL {n} l r) rewrite +-suc n n = bal (convertT l) (convertT (insert x r))
Agda接受这种可能性,但以黄色突出显示等式。我想我需要明确给出我传递给bal
构造函数的两个子树的大小:
insert x (heavyL {n} l r) rewrite +-suc n n = bal {n = suc n} (convertT l) (convertT (insert x r))
但现在我再次收到相同的错误消息!
n + suc n != suc (n + n) of type ℕ
when checking that the expression
bal {n = suc n} (convertT l) (convertT (insert x r)) has type
T .A (suc (suc (n + n)))
我没有想法。我确定我犯了一个愚蠢的错误。我究竟做错了什么?我需要做些什么来定义insert
类型检查?
答案 0 :(得分:4)
你的rewrite
尝试几乎可行,但它所使用的平等方向是错误的。为了让它在正确的方向上工作,你可以翻转它:
open import Relation.Binary.PropositionalEquality
-- ...
insert x (heavyL {n} l r) rewrite sym (+-suc n n) = bal l (insert x r)
或使用with
子句:
insert x (heavyL {n} l r) with bal l (insert x r)
... | t rewrite +-suc n n = t
另一种可能性是在右侧自己进行替换:
open import Relation.Binary.PropositionalEquality
-- ...
insert x (heavyL {n} l r) = subst (T _) (+-suc (suc n) n) (bal l (insert x r))
答案 1 :(得分:4)
你只需要在另一个方向上重写:
open import Relation.Binary.PropositionalEquality
insert : ∀ {A n} -> A -> T A n -> T A (suc n)
insert x empty = leaf x
insert x (leaf y) = bal (leaf y) (leaf y)
insert x (bal l r) = heavyL (insert x l) r
insert x (heavyL {n} l r) rewrite sym (+-suc n n) = bal l (insert x r)
您可以使用Agda的大孔机械来弄清楚发生了什么:
insert x (heavyL {n} l r) = {!!}
在类型检查并将光标放在{!!}
之后,您可以输入C-c C-,
并获取
Goal: T .A (suc (suc (n + n)))
————————————————————————————————————————————————————————————
r : T .A n
l : T .A (suc n)
n : ℕ
x : .A
.A : Set
将bal l (insert x r)
放入洞中并输入C-c C-.
后,您将获得
Goal: T .A (suc (suc (n + n)))
Have: T .A (suc (n + suc n))
————————————————————————————————————————————————————————————
r : T .A n
l : T .A (suc n)
n : ℕ
x : .A
.A : Set
所以存在不匹配。 rewrite
修正了它:
insert x (heavyL {n} l r) rewrite sym (+-suc n n) = {!bal l (insert x r)!}
现在输入C-c C-.
(在类型检查之后)给出
Goal: T .A (suc (n + suc n))
Have: T .A (suc (n + suc n))
————————————————————————————————————————————————————————————
r : T .A n
l : T .A (suc n)
x : .A
.A : Set
n : ℕ
您可以在洞中输入C-c C-r
来完成定义。
Agda接受这种可能性,但强调了这个等式 黄色。
Agda无法推断n
中的m
和n + suc m
,因为n
上存在模式匹配。在Agda邮件列表上有一个thread隐式参数。