根据this course,所有构造函数(对于归纳类型)都是单射的和不相交的:
......类似的原则适用于所有归纳定义的类型:全部 构造函数是单射的,价值观是由不同的 建设者永远不会平等。对于列表,cons构造函数是 injective和nil与每个非空列表不同。对于 布尔,真假都是不平等的。
(以及基于此假设的inversion
策略)
我只是想知道我们如何知道这个假设?
我们如何知道,例如,我们无法根据
定义自然数1)继任者,也许是" Double"像这样的构造函数:
Inductive num: Type :=
| O : num
| S : num -> num
| D : num -> num.
和
2)一些plus
函数,以便可以通过两个不同的构造函数序列/路由2
和S (S O)
来访问一个数字D (S O)
?
Coq的机制是什么,以确保上述内容永远不会发生?
P.S。
我不是建议上面的num
示例是可能的。我只是想知道是什么让它变得不可能。
由于
答案 0 :(得分:6)
在Coq中定义归纳数据类型时,基本上就是这样
定义树类型。每个构造函数都给出了一种节点
允许在你的树中发生,它的参数决定了
子节点和该节点可以拥有的元素。最后,功能
在归纳类型上定义(带有match
子句)可以检查
用于生成该类型值的构造函数
任意方式。这使得Coq构造函数与之非常不同
例如,您在OO语言中看到的构造函数。一个东西
构造函数实现为初始化a的常规函数
给定类型的值;另一方面,Coq构造函数是
枚举我们类型的表示的可能值
允许。为了更好地理解这种差异,我们可以比较一下
我们可以在传统OO中的对象上定义不同的函数
语言,以及Coq中归纳类型的元素。我们来使用吧
以num
类型为例。这是一个面向对象的定义:
class Num {
int val;
private Num(int v) {
this.val = v;
}
/* These are the three "constructors", even though they
wouldn't correspond to what is called a "constructor" in
Java, for instance */
public static zero() {
return new Num(0);
}
public static succ(Num n) {
return new Num(n.val + 1);
}
public static doub(Num n) {
return new Num(2 * n.val);
}
}
这是Coq中的定义:
Inductive num : Type :=
| zero : num
| succ : num -> num
| doub : num -> num.
在OO示例中,当我们编写一个带Num
的函数时
参数,没有办法知道哪个“构造函数”被用来
产生该值,因为此信息不存储在
val
字段。特别是Num.doub(Num.succ(Num.zero()))
和
Num.succ(Num.succ(Num.zero()))
将是相等的值。
另一方面,在Coq示例中,事情发生了变化,因为我们可以
确定使用哪个构造函数来形成num
值,这要归功于
match
声明。例如,使用Coq字符串,我们可以编写
像这样的函数:
Require Import Coq.Strings.String.
Open Scope string_scope.
Definition cons_name (n : num) : string :=
match n with
| zero => "zero"
| succ _ => "succ"
| doub _ => "doub"
end.
特别是,即使您对构造函数的意图
表示应succ (succ zero)
和doub (succ zero)
“在道德上”相等,我们可以通过应用cons_name
来区分它们
对他们有用:
Compute cons_name (doub (succ zero)). (* ==> "doub" *)
Compute cons_name (succ (succ zero)). (* ==> "succ" *)
事实上,我们可以使用match
来区分succ
以及任意方式doub
:
match n with
| zero => false
| succ _ => false
| doub _ => true
end
现在,Coq中的a = b
意味着我们没有可能的方式
区分a
和b
。上面的示例说明了为什么doub
(succ zero)
和succ (succ zero)
不能相等,因为我们可以
编写不尊重我们想到的意义的函数
当我们写这种类型。
这解释了为什么构造函数是不相交的。他们是无意义的 实际上也是模式匹配的结果。例如, 假设我们想要证明以下陈述:
forall n m, succ n = succ m -> n = m
我们可以用
开始证明intros n m H.
引导我们
n, m : num
H : succ n = succ m
===============================
n = m
请注意,此目标是通过简化等同于
n, m : num
H : succ n = succ m
===============================
match succ n with
| succ n' => n' = m
| _ => True
end
如果我们rewrite H
,我们会获得
n, m : num
H : succ n = succ m
===============================
match succ m with
| succ n' => n' = m
| _ => True
end
简化为
n, m : num
H : succ n = succ m
===============================
m = m
在这一点上,我们可以用反身性来总结。这种技术是
相当普遍,实际上是inversion
所做的核心。
答案 1 :(得分:1)
没有:构造函数O
,S
和D
确实是不相交的和内射的,但是你头脑中num
的语义不是,一个函数,单射。
这就是为什么num
通常被认为是对自然数的不良表示的原因:努力达到等价是非常烦人的。