ML versus Racket.
General semantics 上的异同:
- similarities:
- have constructs that encourage a functional style.
- avoiding mutations, while allowing it where appropriate.
- using first-class closures.
- …
- differences:
- different syntax: ML’s pattern matching, Racket’s accessor functions for structs.
- Racket’s mutiple variants of let-expressions (
let,let*,letrec) - (most important) ML has a static type system that Racket does not!
ML’s static type system rejects lots of programs before running them by doing type-checking and reporting errors. To do so, ML enforces certain restrictions (e.g., all elements of a list must have the same type). As a result, ML ensures the absence of certain errors at compile time.
理解两个语言区别的最好方式是从其中一个语言的角度审视另一个语言。抛开语法的桎梏,我们站在 ML 的视角,研究它的静态类型系统会对 Racket 的程序作何反应。
1 | (define (f y) (+ y (car y))) |
ML 的静态类型系统可以识别到以上程序的类型错误并在编译时报告错误,阻止该程序进入运行阶段。而 Racket 只有该函数被调用时才会弹出 run-time error。
1 | (define xs (list 1 #t "hi")) |
然而 ML 也会 reject 一些在 Racket 视角下没有 bug 的程序;如上例,这个程序违反了 ML 的类型规则:list 中的所有元素类型应该保持一致。但这样的 list 定义在 Racket 中完全没有问题。
从这个角度看,我们能够得出这样的结论:ML is roughly a subset of Racket. Programs that run produce similar answers, but ML rejects many more programs as illegal, i.e., not part of the language.
那么,一个 ML 程序员又会如何看待 Racket 呢?他可能会认为 Racket is just ML where every expression is part of one big datatype.
1 | datatype theType = Int of int |
Racket 中的每个表达,都被某个 constructor 隐式的 (implicitly) 包装入这个 big datatype 中。例如 42 实际上是 (Int 42);这能够使得每个表达的结果都是 theType 类型。
并且,函数将检查其参数是否有正确的 constructors (in other words, “tags”),如果不合法,弹出错误;反之将调用函数的结果也隐式的包装在对应的 theType 类内。例如,(+ a b) 将检查 a 与 b 是否有标签 Int;如果有,则返回结果 (Int a+b)。
1 | fun car v = case v of Pair(a, b) => a | _ => raise ... (* give some error *) |
这是另外一个例子:我们用 ML 来展示 Racket 中存在的 implicit run-time pattern matching。由于这种 “secret pattern-matching” 是不对程序员暴露的,Racket 提供了类型检查函数例如 pair?。
The fact that we can think of Racket in terms of theType suggests that anything you can do in Racket can be done, perhaps more awkwardly, in ML: The ML programmer could just program explicitly using something like theType definition above.