前言
大多数编程语言在处理的静态和动态阶段之间表现出阶段差异。静态阶段包括解析和类型检查,以确保程序是格式良好的;动态阶段包括执行格式良好的程序。当格式良好的程序在执行时表现良好时,这种语言就是安全的。
静态阶段由一组规则组成的静态图表来指定,这些规则用于派生类型判断,这些判断表明表达式是某种类型的格式良好的表达式。类型通过“预测”部分的执行行为的某些方面来调节程序的组成部分之间的交互,这样我们就可以确保它们在运行时正确地组合在一起。类型安全告诉我们这些预测是正确的;如果没有,则认为静态定义不正确,并且认为该语言不适合执行。
在这一章中,我们介绍了一种简单静态表达式语言,作为贯穿本书的方法的一个例证。
4.1 语法
在定义一种语言时,我们将主要关注它的抽象语法,具体由一组操作符及其特征来指定。抽象语法系统地、明确地描述了语言的层次结构和绑定结构,并被认为是语言的正式表示。然而,为了清晰起见,指定最小的具体语法约定也是有用的,而不必费心为其设置完全精确的语法。
我们将通过语法图表来实现这两个目的,语法图表的意义最好通过例子来解释。下表总结了。
这图定义了两个类:涉及到
,
涉及到
。图表定义了一组操作和他们的参数数量。例如,它指定操作符
具有:
,后者指定它有两个
参数,并在第二个参数中绑定一个
变量。
4.2 类型系统
类型系统的作用是对短语的构成施加约束,这些短语对它们出现的上下文很敏感。例如,表达式是否合理取决于变量
是否被限制在表达式的周围上下文中具有
类型。实际上,这个例子是一般情况的一个例子,因为关于表达式上下文所需的惟一信息是表达式所在范围内的变量的类型。因此,静态
包括对形式如:
的常规假言判断的归纳定义。 其中
是一组有限的变量,对于每一个假设:
,
是一个类型上下文,由形式:
的假设组成。我们依赖于排版的习惯来确定变量集,使用字母
和
来表示它们。对于
这意味着:对于任意类型
的表单
,在
中没有对应的假设。在这种情况下,我们说
对于
是不相关的。
在规则(4.1h),我们默认假设变量,还没有在
中声明。总是可以满足此条件,选择一个合适的代表α-等价类的表达式。
通过类型化归纳,很容易检查每个表达式最多有一种类型,这就是应用于规则的规则归纳(4.1)。
引理4.1:对于每一个输入上下文
和表达式
,至少存在一个
,使得
。
类型规则是语法导向的,因为每种表达式都只有一个规则。因此,很容易给出与相应类型规则所表示的充分条件相反的表达式的输入必要条件。
引理4.2:(输入反转)假设
。如果
,则:
,
,
。(语言的其他结构也是如此。)
4.3 结构性质
静态语义具有一般假设判断的结构性质
引理4.3:对于
和
类型,如果
,则:
。
证明:为了证明引理4.3。我们将假设在和
的条件下,给出
。
从而推导出:
引理4.4:(置换)如果
,且
,则:
。
证明:通过对推导的归纳。我们假设在
和
的条件下,
。我们根据引理4.3得到:
选择:
根据规则(4.1h):
从编程的角度来看,引理4.3允许我们在任何情况下使用一个表达式,结合其自由变量:如果在上下文中,表达式
是类型良好的,所以,我们可以使用
导入任何包含
的上下文。换句话说,在表达式
需要的变量之外引入新变量并不会使
本身失效;它保持良好的形式和类型。更重要的是,引理4.4表达了模块化和链接的重要概念。我们可以把表达式
和
看作是一个更大的系统的两个组件,在这个系统中,
是实现
的一个客户机。实现必须是指定的类型,以满足客户端的假设。如果是这样,那么我们可以将它们联系起来形成复合体系
。这个实现本身可能是另一个组件的客户端,它由一个变量
表示,该变量在链接期间被该组件替换。当所有这些变量都已实现时,结果是一个准备执行的封闭表达式(求值)。
引理4.4的逆命题称为分解。它指出,任何(大型)表达式都可以通过引入一个变量来协调它们的交互,从而分解为客户机和实现者。
引理4.5:(分解)假如:
,对于每一个形式为:
的每一个
。我们有:
。
语言的结构可以分为两种形式:引入和消除。类型的引入形式确定该类型的值或规范形式。消除形式决定如何操作一个类型的值以形成另一个(可能相同的)类型的计算。
在语言中,
类型的引入形式是数字,而
类型的引入形式是文字。
类型的消除形式是加法和乘法,
类型的消除形式是串联和长度。
一旦我们在第5章中定义了动态语言,这种分类的重要性就会变得很明显。然后我们会看到消元形式与引入形式是相反的,因为它们“拆解”了引入形式所具有的“组合”。“一种语言的静态和动态的一致性表达了类型安全的概念,这是第六章的主题。