编程语言课程笔记(终)
Static VS Dynamic 静态动态对比 一门语言称之为静态或是动态,指的是它在什么时候进行类型检查。 ML VS Racket 可以有两种看法: ML 某种程度上是 Racket 的子集,Racket 支持的写法更多,因为一部分 Racket 中允许的表达式不会被 ML 的静态检查通过。比如 if 语句的两个分支返回不同的类型。 Racket 里的变量其实不是“没有类型”,而是只有“一个类型”。 datatype theType = (* 所有的变量类型都一样,所以没有必要标注或是检查 *) Int of int | (* 变量所持有的值是有不同类型的,即运行时值是带有所谓标签的 *) String of string | Pair of theType * theType | Fun of theType -> theType | ... Static Checking 静态检查 静态检查处在语法分析之后,程序运行之前,所以称之为“编译时检查”(但这跟语言用编译器还是解释器没有关系)。静态检查如何作用是语言定义的一部分,有的语言做得多有的语言做得少,给定检查规则,也可以自己实现工具来做期望的检查。 实现静态检查的方式主要是类型系统,但类型系统只是实现静态检查的方式,而不是静态检查的目的。静态检查的目的是防止错误,但这个错误的范围很有限,比如大多数静态检查都不会检查数组是否越界,静态检查也没法告诉你某个地方你应该用乘法但是你用了加法。它能防止的是,字符串不能与整型相除,某个类型不能做某些事情。 相比于动态检查(即给值加上标签,运行时进行检查),静态检查其实拒绝了一部分并不会出错的程序。这里值得讨论的东西在于,首先,你如何定义错误。 考虑一个简单的“错误”: 3 / 0,你可以在下面这些时间点阻止这个“错误” Keystroke-time: 在编辑这段代码的时候阻止,意味着你写下 3 / 0 就已经是一个错误 Compile-time: 当静态检查器“看到”这段代码的时候阻止,意味着这段代码不应该出现 Link-time: 当发现这段代码会被调用的时候阻止,意味着调用这段代码是一个错误 Run-time: 当执行这段代码的时候阻止,即被 0 除这个运算是一种错误 Even-later 执行这段代码没有错,但如果用到了这个运算返回的结果(可以是一个表示未定义的标识,比如 undefined),才是一种错误(比如把 undefined 用作数组下标) 甚至用返回的结果来做计算也没错,3 / 0 应该表示正无穷,同样的 tan(π/2) 也表示正无穷,你可以让它参与计算(事实上, Racket 里 (/ 3.0 0.0) 就返回 +inf.0) 第二个值得讨论的就是如何定义正确,怎么判断类型检查是符合语言定义的。 ...