我正在学习软件基础中的IndProp和Adam Chlipala的第4章,并且在理解归纳命题时遇到困难。
为使示例运行起来,让我们使用:
Inductive ev : nat -> Prop :=
| ev_0 : ev 0
| ev_SS : forall n : nat, ev n -> ev (S (S n)).
我认为我确实可以使用Set
来理解“正常”类型,例如:
Inductive nat : Set :=
| O : nat
| S : nat -> nat.
诸如O
之类的东西只是返回类型为nat
的单个对象,而S O
正在获取类型为nat
的对象,并返回另一个不同的相同类型nat
之一。通过不同的对象,我想我的意思是它们具有不同的价值。
我困惑的是归纳道具的构造函数与归纳类型Set
到底有何不同。对于Set
来说,它们似乎只是充当函数,该函数接受值并返回该类型的更多值。但是对于归纳命题,我很难弄清楚它们的作用。
例如,以ev_0
为例。我假设这是命题对象(值)ev 0
的名称。由于ev 0
本身必须是Prop
的命题。但是到底是什么使它成为现实呢?如果这是一个命题,那我可能是错误的。我想这个归纳使我感到困惑。 ev
是“返回某种类型(命题)对象的函数”,因此ev 0只是一个命题,但是我们没有说ev 0
的含义(与我对自然数的定义不同,基本情况很清楚它在做什么)。在python中,我希望看到
n == 0: return True
或基本情况下的内容。但是在这种情况下,似乎是指向自身而不是指向True
之类的圆形对象。我知道我有一个基本的误解,但我不知道我到底不了解什么。
同样让我感到困惑的是命名。在nat
中,名称O
对于构建/构造对象至关重要。但是在归纳定义中,ev_0
似乎更像是实际值ev 0
的标签/指针。因此,我认为ev_SS == ev_0 -? ev 2
毫无意义,但我不知道为什么。此处的标签与使用set
的归纳类型的标签有何不同?
对于ev_SS
,这更加令人困惑。因为我不知道标签在做什么,这当然使我感到困惑,但是请看这是如何指出的:
forall n : nat, ev n -> ev (S (S n))
因此给定自然数n
,我假设它返回命题ev n -> ev (S (S n))
(我假设forall n : nat
不是命题对象的一部分,而它只是在这里指示构造函数何时返回命题作品)。因此,forall n : nat, ev n -> ev (S (S n))
并不是真正的命题,而是ev n -> ev (S (S n))
。
有人可以帮我弄清楚归纳命题类型在Coq中是如何工作的吗?
请注意,我并不真正了解Set
与Type
之间的区别,但我认为这本身就是另一篇文章。
更多评论:
我还在玩这个,做了:
Check ev_SS
令我惊讶的是:
ev_SS
: forall n : nat, ev n -> ev (S (S n))
我认为这是意外的,因为我没想到ev_SS
的类型(除非我误解了Check
应该做的事情)才是函数本身的定义。我以为ev_SS
是一个构造函数,所以在我看来,我想在这种情况下可以进行映射nat -> Prop
,所以这当然就是我所期望的类型。
答案 0 :(得分:4)
因此,首先,对此感到困惑是很正常的,但它可能比您想的要简单!
您会混淆两个截然不同的概念,所以让我们一次将它们融合为一个。首先,您提到ev 0
是一个命题,并想知道是什么使它成为现实。您将在某个时候了解到命题和类型是相同的事物,Prop
与Set
和Type
之间的区别并不是这些事物本质上是不同的。
因此,当您定义类型(或命题)nat
时,您正在创建一个新类型,并描述其中存在的值。您声明存在一个值O
,即nat
。并且您声明在传递S
时有一个参数化值nat
,即nat
。
以同样的方式,当您定义类型(或命题)ev
时,您正在创建一个新类型(嗯,它实际上是由类型nat
的索引的一系列类型),并描述这些ev
类型中存在哪些值。您声明存在值ev_0
,即ev 0
。并且您声明在传递ev_SS
和ev (S (S n))
时有一个参数化值n : nat
,即ev n
。
因此,您通过在其中创建价值的方法使该命题成为真实。您也可以通过不使用构造函数或使用永远无法调用的构造函数来定义错误的命题:
Inductive MyFalse1 := . (* no constructor! *)
Inductive MyFalse2 :=
| TryToConstructMe : False -> MyFalse2
| ThisWontWorkEither : 0 = 1 -> MyFalse2
.
我已经声明了两种现在的类型(或命题),但是无法见证它们,因为它们要么没有构造函数,要么根本没有办法调用这些构造函数。
关于事物的命名,ev_0
,ev_SS
,O
和S
都是相同的实体:构造函数。我不确定您为什么认为ev_0
是指向值ev 0
的指针。
没有任何意义将ev n
分配为一个命题,除了它是一个命题,如果有一种方法可以构造类型为ev n
的值,则该命题可能为true;否则,则为false无法构造类型为ev n
的值。
但是,请注意,ev n
精心设计为适合偶数的n
居住,而恰好是奇数的n
不居住的。从这个意义上说,ev
抓住了一个命题。如果收到类型为ev n
的值,则它实际上断言n
是偶数,因为类型ev n
仅包含偶数的居民:
ev 0
包含1个居民(ev_0
)ev 1
包含0个居民ev 2
包含1个居民(ev_SS 0 ev_0
)ev 3
包含0个居民ev 4
包含1个居民(ev_SS 2 (ev_SS 0 ev_0)
)最后,对于Set
,Prop
和Type
之间的区别,您可以在其中创建归纳类型的所有Universe,但它们都具有某些限制。
Prop
可以优化代码生成。从本质上讲,这是程序员您将某种类型标记为仅用于验证目的,而从未用于计算目的的一种方式。结果,类型检查器将迫使您从不对Prop
范围内的证明进行计算,并且代码生成将知道,由于这些证明不参与计算行为,因此可以丢弃这些证明。>
Set
只是对Prop
的限制,以避免处理Universe级别。直到以后的学习过程中,您才真正需要了解这一点。
您真的应该尝试不要将Prop
视为与Set
不同的神奇事物。
以下内容可能会对您有所帮助:我们可以用完全等效的方式重写nat
和ev
的定义,如下所示:
Inductive nat1 : Set :=
| O : nat1
| S : nat1 -> nat1
.
(* is the same as *)
Inductive nat1 : Set :=
| O : nat1
| S : forall (n : nat1), nat1
.
(* and *)
Inductive ev : nat -> Prop :=
| ev_0 : ev 0
| ev_SS : forall (n : nat), ev n -> ev (S (S n))
.
(* is the same as *)
Inductive ev : nat -> Prop :=
| ev_0 : ev 0
| ev_SS : forall (n : nat) (e : ev n), ev (S (S n))
.
每次您看到类似a -> b
的类型时,它实际上是forall (_ : a), b
的简写形式,也就是说,我们期望输入a
的类型,并返回输出输入b
。
之所以在forall (n : nat)
中写ev_SS
是因为我们必须为第一个参数命名,因为第二个参数的类型取决于它(第二个参数的类型为ev n
)。
答案 1 :(得分:3)
如果将Prop
替换为Set
,则表示您理解以下定义:
Inductive ev' : nat -> Set :=
| ev_0' : ev' 0
| ev_SS' : forall n : nat, ev' n -> ev' (S (S n)).
对于每一个n : nat
,我们都将ev' n
视为具有某些元素或可能没有元素的类型。因为这是一个归纳定义,所以我们可以判断ev' 0
的元素是什么:唯一的元素是ev_0'
(或更确切地说,类型ev' 0
的每个封闭项都将计算为{ {1}})。 ev_0;
没有元素,但是ev_0 1
有一个元素,即ev 2'
。实际上,一点点思考表明ev_SS' 0 ev_0'
是空还是单例,这取决于ev n
是偶数还是奇数。
当我们从n
切换到Set
时,这是完全一样的,除了读数不同:Prop
是(大)类型的类型,Set
是也是一种类型(它们是 universes )。 Prop
的每个元素都是一个类型(但我们更喜欢将其称为“命题”),其中包含一些元素(但是我们更喜欢将它们称为“证明”)。所以考虑一下:
Prop
对于每一个Inductive ev : nat -> Prop :=
| ev_0 : ev 0
| ev_SS : forall n : nat, ev n -> ev (S (S n)).
,我们都认为n : nat
是ev n
具有属性n
的陈述,无论该属性可能是什么。对于任何给定ev
,可能会有n
的证明,在这种情况下ev n
具有属性n
,或者可能没有这样的证明,在这种情况下{ {1}}没有属性ev
。因为这是一个归纳定义,所以我们可以说出n
的证明是什么:它们都计算为ev
。没有ev_0
的证明,但是有ev_0'
的证明,即ev_0 1
。实际上,一点点思考表明ev 2
有证据,并且只有ev_SS 0 ev_0
是偶数。现在我们了解到ev n
是“偶数”的属性。
这被称为“类型命题”。
我们观察到n
仅包含一个元素ev
。类型ev' 0
也仅包含一个元素ev_0'
。这是否意味着unit
和tt
相等?不,但是它们是等效的,因为我们可以提供彼此相反的函数ev' 0
和unit
。
我们可以对ev' 0 -> unit
提出同样的问题:它等于unit -> ev' 0
吗?不,但是它们是等效的,因为我们可以证明其含义ev 0
和True
。
人们开始怀疑ev 0 -> True
和True -> ev' 0
之间的区别是什么。对于类型Prop
,其所有元素均被视为相等,即Coq不允许我们对其进行区分。 (这是一个教学上的谎言,因为实际上Coq对于Set
的所有元素是否都相等是不可知的,但是也许最好不要现在就进入。 )