我敢肯定,对于静态语言的许多“优雅”问题,静态类型检查本身并不是罪魁祸首,而是该语言实现的静态类型系统缺乏表达能力以及编译器的有限能力。如果这样做“更正确”(例如在 Haskell 中),那么程序突然变得简洁、优雅……并且比动态对应的程序更安全。
这是一个插图(C++ 特定的,抱歉):C++ 是如此强大,它使用它的模板类系统实现了一种元语言。但是,仍然很难声明一个非常简单的函数:
template<class X,class Y>
? max(X x, Y y)
有大量可能的解决方案,例如 ?=boost::variant<X,Y>
或计算 ?= is_convertible(X,Y)?(X:is_convertible(Y,X):Y:error)
,但没有一个真正令人满意。
但是现在想象一个预处理器,它可以将输入程序转换为等效的延续传递样式形式,其中每个延续都是一个可调用对象,它接受所有可能的参数类型。max 的 CPS 版本如下所示:
template<class X, class Y, class C>
void cps_max(X x, Y y, C cont) // cont is a object which can be called with X or Y
{
if (x>y) cont(x); else cont(y);
}
问题消失了,max 调用了一个接受 X 或 Y 的 continuation。所以,对于 max 有一个静态类型检查的解决方案,但是我们不能用它的非 CPS 形式表达 max,untransform(cps_max)
可以说是未定义的。所以,我们有一些可以正确处理的论点max
,但我们没有办法这样做。这是缺乏表现力。
2501 更新:
假设有一些不相关的类型 X 和 Y,并且有一个bool operator<(X,Y)
. 应该max(X,Y)
返回什么?让我们进一步假设,X 和 Y 都有一个成员函数foo();
。我们怎样才能写出:
void f(X x, Y y) {
max(X,Y).foo();
}
返回 X 或 Y 并在结果上调用 foo() 对于动态语言来说没有问题,但对于大多数静态语言来说几乎是不可能的。但是,我们可以通过重写 f() 来使用 cps_max 来获得预期的功能:
struct call_foo { template<class T> void operator(const T &t) const { t.foo(); } };
void f(X x, Y y) {
cps_max(x,y,call_foo());
}
所以这对于静态类型检查来说不是问题,但它看起来很丑陋,并且除了简单的示例之外不能很好地扩展。因此,这种静态语言缺少的是我们无法提供静态且可读的解决方案。