[]
类型:语法导向、反演与唯一性
[] 类型:语法导向、反演与唯一性
1 类型判断
这个定义有三处值得逐一拆开:
- “存在某个 ”:良类型只要求有一个类型,不要求事先指定是哪一个。 是良类型的,因为 ;它不是 并不影响这一点。
- “有一棵推导”:和 一样, 成立当且仅当能用 [定义] 类型与类型判断 中七条规则的实例搭出一棵以它为根的有限树。所以说一个项良类型,就是说能写出这样一棵树;说它不良类型,就是说无论怎样搭都搭不出来。后者是一个关于所有可能推导的否定命题,不能仅凭尝试构造某棵推导树失败就断言它不良类型。要证明它不良类型,我们需要说明任何合法的推导都不可能成立。在这里,可以通过分析类型规则以及它们所要求的前提来完成证明,下面的例子会演示。
- 良类型是一个静态性质:在我们目前讨论的语言中, [定义] 类型与类型判断 中的类型判断只依赖项的结构与类型规则,不需要执行 [定义] 一步归约 中的 归约。换句话说,我们不必运行一个项,就能判断它是否良类型。这正是静态类型检查(static type checking)的基础(与动态检查的对照见 [附注] 静态类型与动态类型 )。不过,类型判断与求值虽然是两套不同的规则,它们之间是否协调一致,还需要通过 [推论] 类型安全 的类型安全性定理来保证。
读法和 [定义] 一步归约 完全一样:横线上是前提,横线下是结论,、 是元变量( [约定] 元变量 )。所以“对类型推导归纳”也照样成立,证明与 [定理] 对推导归纳 相同,不再重复。
2 反演:从结论倒推前提
先看这组规则的结论。每条规则的结论都形如“某个项 : 某个类型”,结论左边的项有一个最外层构造子(、、 等),这就是它的形状。把七条规则按结论左边的形状列出来:
| 项的形状 | 结论里出现这个形状的规则 | 条数 |
| T-True | 1 | |
| T-False | 1 | |
| T-Zero | 1 | |
| T-If | 1 | |
| T-Succ | 1 | |
| T-Pred | 1 | |
| T-IsZero | 1 |
“每种项的形状恰好出现在一条规则的结论里”指的就是这张表:第三列全是 。既不是 (否则那种形状的项永远没有类型),也不大于 (否则同一个项可能有好几种定型方式)。这样的规则称为语法导向的(syntax-directed):项的语法形状直接指定了该用哪条规则,不需要猜,也不需要回溯。
作为对照,下面两种情形都不是语法导向的:
- 如果再加一条规则 (“自然数可以当布尔值用”),它的结论左边是任意项 ,不看形状。这时 可以由它推出, 这一行就有了两条规则可选。
- 如果把 T-If 拆成两条,一条要求条件是 ,一条要求条件是 ,那么 这种条件不是常量的项就一条规则都没有,这一行变成 。
语法导向带来一个很方便的推理方式:知道了 成立,就能断定推导的最后一步用的是哪条规则,于是那条规则的前提也都成立。例如,知道 成立,查表可知最后一步必然是 T-Succ,于是立刻得到 且 ,不用知道推导的其余细节。 [例] 良类型与不良类型 里的两个例子也是这样做的:每一步都只有一条规则可选。
反演看起来平淡无奇,却是后面所有证明的发动机。它把“存在一棵推导”这种不知道细节的事实,拆成关于子项的具体信息。保型证明例如从 开始,必须先知道 、,再继续取出 ,才能证明删去外层构造以后类型还在。没有反演,就不能凭外观擅自断言子项有哪些类型;增加子类型规则时这个推理尤其需要重证,下面的附注会给出原因。
语法导向并不单凭“每种形状只有一条规则”就让输出类型自动唯一:T-If 的 还要从分支中确定。下面的定理补上这个保证,说明
type_of
type_of
可以忠实地返回一个类型,而不必返回类型集合。没有唯一性,单个返回值可能只挑出了众多类型之一。例如带子类型的 Kotlin 中,一个
Int
Int
表达式也可以出现在需要
Number
Number
或
Any
Any
的位置;那样的系统通常要另外区分推断出的类型和它能被接受为的所有类型,不能照搬本文的结论。
3 练习:类型
本节练习(4题)
练习4.1:从结论搭起一棵类型推导
为 写出完整的类型推导树,并指出推导中有没有哪一步需要先运行这个项。使用 [定义] 类型与类型判断 。提示
最外层用 T-If;条件从 T-IsZero 开始,两个分支都应得到 。参考答案
每个叶子都是 T-Zero,两个分支具有同一个类型 。定型过程中没有执行任何 归约;条件最后会变成什么,不是搭这棵推导的前提。
练习4.2:证明没有类型,而不是只说“检查失败”
证明以下三项对任何 都不能得到 :、、。每项给出无法满足的具体前提;其中有没有一项实际能归约到值?参考答案
- 若有类型,最后只能用 T-Succ,要求内部 为 。T-If 随即要求 ,与 T-False 的结论不符。
- 若有类型,T-If 要求条件为 ,但 的类型只能是 。
- 若有类型,外层 T-IsZero 要求内层为 ,但内层 T-IsZero 的结论只能是 。
这三种分析排除了所有可能的最后规则,所以是不存在推导的论证。第一个项却可以归约到 :实际没有执行坏分支。这再次说明“不良类型”并不等于“这次运行必定出错”。
练习4.3:反演不需要看见整棵推导
设 、、 是任意项。只知道 ,却没有拿到它的推导树。你仍能确定 、、、 的哪些类型信息?逐次标明使用 [引理] 反演 的哪一条。提示
先反演 T-If,再连续反演 T-Pred 和 T-Succ;整项的 会被其中一个分支确定。参考答案
T-If 给出 、、。T-Pred 给出 以及 ;T-Succ 再给出 。代回得到 。
因而一定有 、、、。这只提取已有推导必须满足的前提,并没有假定 或 已经求值,也不需要猜原推导的其他细节。