What is Static Checking?
What is usually meant by “static checking” is anything done to reject a program at compile-time.
Compile-time is the time after a program (successfully) parses but before it runs.
注意,compile-time 是一个时间概念,虽然名字中含有 compile,但它与语言实现采用的是 interpreter 还是 compiler 无关。这进一步印证了我们之前所提到的语言实现并不是语言本身的 feature 这一观念。
The most common approach to define a language’s static checking is via a type system.
type system 中包含一系列 typing rules,例如 (以 ML 为背景):每个变量都有一个类型,条件语句的分支有着相同的类型,list 中的元素类型一致等等。static checking 则检查程序是否遵循了这些规则。
注意在 ML 中,type inferencer 也是 static checking 的一部分,它对程序进行推断,判断是否存在一种可能的 type annotations,使得程序能够遵循 type system 中定义的规则。
The purpose of static checking is to reject programs that “make no sense” or “may try to misuse a language feature”.
static checking 的确能够达到它设计之初的目的,但它并不是完美的;
- There are errors a type system typically does not prevent (such as array-bounds errors).
- There are errors a type system cannot prevent unless given more information about what a program is supposed to do. (ex. wrongly calls
+instead of*) - The necessary trade-off. the static checker has to reject some programs that would not do anything wrong.
值得注意的是第三点:Racket 的动态检查 (dynamic checking, i.e., run-time checking) 通过赋予并检测值的标签实现 (即上一节提到的 secret pattern-matching),这使得它在运行时发现程序的错误。
而静态检查则能在程序运行之前排除某些有问题的程序,这样做的代价 (trade-off) 是它将会拒绝某些实际上没有问题的程序 (例如拥有不同类型元素的 list)。
The typical points at which to prevent an error are compile-time and run-time. However, it is worth realizing that there is really a continuum of eagerness about when we declare something an error.
实际上,静态检查与动态检查并非是对立的概念,对于不同类型的错误,我们可以选择在 compile-time 将其检出,也可以选择等到 run-time 时再弹出错误;在这两个时间段之外进行处理也是一个可行但非典型的选择。
如何掌握好 static checking 的 trade-off,平衡其检出错误的效率与语言的灵活性,才是一个更加实际且有意义的命题。