所以,首先,你对此感到困惑是正常的,但它可能比你想象的要简单!
您对两个不同的概念感到困惑,所以让我们逐一讨论它们。首先,你提到ev 0
是一个命题,并想知道是什么让它成为现实。在某些时候你会了解到命题和类型是相同的东西,并且它们之间的区别Prop
and Set
and Type
并不是说这些东西本质上是不同的。
因此,当您定义类型(或命题)时nat
,您正在创建一个新类型,并描述其中存在哪些值。你声明有一个值O
, 这是一个nat
。并且你声明有一个参数化值S
, 这是一个nat
,当通过一个nat
.
同样,当你定义类型(或命题)时ev
,您正在创建一个新类型(嗯,它实际上是一个由类型值索引的类型系列nat
),并描述其中存在哪些值ev
类型。你声明有一个值ev_0
,这是一个ev 0
。并且你声明有一个参数化值ev_SS
,这是一个ev (S (S n))
,当通过一个n : nat
and an ev n
.
So you通过在其中创造价值的方式使该主张成为现实。您还可以通过没有构造函数或具有永远无法调用的构造函数来定义伪命题:
Inductive MyFalse1 := . (* no constructor! *)
Inductive MyFalse2 :=
| TryToConstructMe : False -> MyFalse2
| ThisWontWorkEither : 0 = 1 -> MyFalse2
.
我已经声明了两个 now 类型(或命题),但无法见证它们,因为它们要么没有构造函数,要么无法调用这些构造函数。
现在关于事物的命名,ev_0
, ev_SS
, O
, and S
都是同一种实体:构造函数。我不知道你为什么这么想ev_0
是一个指向值的指针ev 0
.
没有赋予任何意义ev n
作为一个命题,除非有一种方法可以构造具有类型的值,否则它可能是正确的命题ev n
,如果无法构造具有类型的值,则可能为 falseev n
.
但请注意ev n
经过精心设计,正是为了满足这些需求n
s 是均匀的,并且正是那些无人居住的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)
)
- etc.
最后,对于之间的区别Set
, Prop
, and Type
,这些都是您可以在其中创建归纳类型的宇宙,但它们有一定的限制。
Prop
实现代码生成的优化。从本质上讲,它是程序员将某种类型标记为仅用于验证目的而不用于计算目的的一种方法。因此,类型检查器将迫使您永远不要在以下范围内计算证明:Prop
宇宙,代码生成将知道它可以丢弃这些证明,因为它们不参与计算行为。
Set
只是一个限制Prop
以避免处理宇宙层面。直到稍后的学习过程中,您才真正需要理解这一点。
你真的应该试着不去想Prop
作为一种神奇的东西不同于Set
.
以下内容可能对您有帮助:我们可以重写以下定义nat
and 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)
in ev_SS
是我们have为第一个参数命名,因为第二个参数的类型将取决于它(第二个参数的类型为ev n
).