Types 依赖对类型

Types 依赖对类型,types,coq,Types,Coq,在《软件基础》中,我们介绍了参数化命题: Definition is_three (n : nat) : Prop := n = 3. Check is_three. (* ===> nat -> Prop *) 这让我想起了依赖对类型,从我们这里可以定义依赖对类型: Inductive sigT {A:Type} (P:A -> Type) : Type := existT : forall x :A, P x -> sigT P. 有人能解释一下它们之间的区别吗?

在《软件基础》中,我们介绍了参数化命题:

Definition is_three (n : nat) : Prop :=
n = 3.
Check is_three.
(* ===> nat -> Prop *)
这让我想起了依赖对类型,从我们这里可以定义依赖对类型:

Inductive sigT {A:Type} (P:A -> Type) : Type :=
existT : forall x :A, P x -> sigT P.
有人能解释一下它们之间的区别吗?还有,在阅读Coq中的HoTT时,它说,既然我们还没有定义命题等式,我们就不能这样做
这里有很多有趣的东西,为什么我们不能在没有命题等式的情况下做任何有趣的事情呢?

现在让我们假设HoTT代码使用了->道具而不是->类型;这两者的区别与你的问题是正交的

参数化命题p:A->Prop是A型元素的简单属性。除了上面简单的is_三命题外,我们还可以用这种方式表示自然数的更复杂属性。例如:

Definition even (n : nat) : Prop :=
  exists p, n = 2 * p.

Definition prime (n : nat) : Prop :=
  n >= 2 /\
  forall p q, n = p * q -> p = n \/ p = 1.
类型sigT A p type允许我们将类型A限制为满足属性p的元素。例如,sigT nat偶数是所有偶数的类型,sigT nat prime是所有素数的类型,等等。在Coq中,属性是更原始的概念,而像sigT这样的子集类型是派生概念

在传统数学中,属性和子集的概念几乎可以混为一谈:说2是素数等于说它属于所有素数的集合。在Coq的类型理论中,情况并非如此,因为作为一个类型的元素不是一个命题:例如,你不能陈述一个定理说2是sigT-nat-prime的元素。以下代码段引发错误:

Lemma bogus : (2 : {x : nat & prime x}).
(* Error: *)
(* The term "2" has type "nat" while it is expected to have type *)
(*  "{x : nat & prime x}". *)
{…&…}是Coq标准库中定义的sigT类型的语法糖

我们能得到的最接近的结果是,可以从该类型中提取2:

Lemma fixed : exists x : {x : nat & prime x}, 2 = projT1 x.
其中projT1是提取依赖对的第一个组件的函数。但是,这比简单地说明2是素数要麻烦得多:

Lemma prime_two : prime 2.
一般来说,参数化命题在Coq中更有用,但也有sigT类型派上用场的情况;例如,当我们只关心满足某个属性的类型的元素时。假设您使用一种二进制搜索树在Coq中实现了一个关联映射。您可以从定义任意树的类型树开始:

这种类型定义了一个二叉树,其节点存储自然数的键值对。为了实现查找元素、更新值等功能,使用这种类型,我们可以保持不变,即树的键被排序,即左子树上的键小于节点的键,右子树的键相反。由于该树的用户不希望考虑不满足该不变量的树,因此我们可以使用类型SIGT树WelyFILL,其中WELLY格式:树-PROP表示上述不变量。主要的优点是,这简化了我们库的接口:不是有一个单独的引理说插入函数保持不变,而是用插入函数本身的类型自动表示;用户甚至不需要争论他们使用接口构建的树是否尊重不变量


至于你的第二个问题,等式是如此的基本,没有它很难定义有趣的属性。例如,上面的属性偶数和素数都是使用等式定义的。

现在让我们假设HoTT代码使用->属性而不是->类型;这两者的区别与你的问题是正交的

参数化命题p:A->Prop是A型元素的简单属性。除了上面简单的is_三命题外,我们还可以用这种方式表示自然数的更复杂属性。例如:

Definition even (n : nat) : Prop :=
  exists p, n = 2 * p.

Definition prime (n : nat) : Prop :=
  n >= 2 /\
  forall p q, n = p * q -> p = n \/ p = 1.
类型sigT A p type允许我们将类型A限制为满足属性p的元素。例如,sigT nat偶数是所有偶数的类型,sigT nat prime是所有素数的类型,等等。在Coq中,属性是更原始的概念,而像sigT这样的子集类型是派生概念

在传统数学中,属性和子集的概念几乎可以混为一谈:说2是素数等于说它属于所有素数的集合。在Coq的类型理论中,情况并非如此,因为作为一个类型的元素不是一个命题:例如,你不能陈述一个定理说2是sigT-nat-prime的元素。以下代码段引发错误:

Lemma bogus : (2 : {x : nat & prime x}).
(* Error: *)
(* The term "2" has type "nat" while it is expected to have type *)
(*  "{x : nat & prime x}". *)
{…&…}是Coq标准库中定义的sigT类型的语法糖

我们能得到的最接近的结果是,可以从该类型中提取2:

Lemma fixed : exists x : {x : nat & prime x}, 2 = projT1 x.
其中projT1是提取依赖对的第一个组件的函数。但是,这比简单地说明2是素数要麻烦得多:

Lemma prime_two : prime 2.
一般来说,参数化建议 tions在Coq中更有用,但也有sigT类型派上用场的情况;例如,当我们只关心满足某个属性的类型的元素时。假设您使用一种二进制搜索树在Coq中实现了一个关联映射。您可以从定义任意树的类型树开始:

这种类型定义了一个二叉树,其节点存储自然数的键值对。为了实现查找元素、更新值等功能,使用这种类型,我们可以保持不变,即树的键被排序,即左子树上的键小于节点的键,右子树的键相反。由于该树的用户不希望考虑不满足该不变量的树,因此我们可以使用类型SIGT树WelyFILL,其中WELLY格式:树-PROP表示上述不变量。主要的优点是,这简化了我们库的接口:不是有一个单独的引理说插入函数保持不变,而是用插入函数本身的类型自动表示;用户甚至不需要争论他们使用接口构建的树是否尊重不变量


至于你的第二个问题,等式是如此的基本,没有它很难定义有趣的属性。例如,上面的偶数和素数属性都是使用等式定义的。

谢谢Arthur!这是一个很好的解释,也间接显示了Coq中Prop和Type之间的差异。因此,证明固定引理意味着在{x:nat&prime x}中找到projT1等于2的依赖对。然后对于质数2的证明,我们使用策略证明所有pq,2=p*q->p=2\/p=1。。这是正确的理解吗?谢谢亚瑟!这是一个很好的解释,也间接显示了Coq中Prop和Type之间的差异。因此,证明固定引理意味着在{x:nat&prime x}中找到projT1等于2的依赖对。然后对于质数2的证明,我们使用策略证明所有pq,2=p*q->p=2\/p=1。。这是正确的理解吗?