我们怎么知道所有Coq构造函数都是单射的和不相交的?

时间:2015-09-19 00:31:15

标签: constructor coq injective-function

根据this course,所有构造函数(对于归纳类型)都是单射的和不相交的:

  

......类似的原则适用于所有归纳定义的类型:全部   构造函数是单射的,价值观是由不同的   建设者永远不会平等。对于列表,cons构造函数是   injective和nil与每个非空列表不同。对于   布尔,真假都是不平等的。

(以及基于此假设的inversion策略)

我只是想知道我们如何知道这个假设?

我们如何知道,例如,我们无法根据

定义自然数

1)继任者,也许是" Double"像这样的构造函数:

Inductive num: Type :=
   | O : num
   | S : num -> num
   | D : num -> num.

2)一些plus函数,以便可以通过两个不同的构造函数序列/路由2S (S O)来访问一个数字D (S O)

Coq的机制是什么,以确保上述内容永远不会发生?

P.S。 我不是建议上面的num示例是可能的。我只是想知道是什么让它变得不可能。

由于

2 个答案:

答案 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意味着我们没有可能的方式 区分ab。上面的示例说明了为什么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)

没有:构造函数OSD确实是不相交的和内射的,但是你头脑中num的语义不是,一个函数,单射。

这就是为什么num通常被认为是对自然数的不良表示的原因:努力达到等价是非常烦人的。