Я уверен, что во многих проблемах «элегантности» статических языков не виновата сама проверка статических типов, но недостаточная выразительность реализованной в языке системы статических типов и ограниченные возможности компилятора.Если это сделано «правильно» (как, например, в 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. Итак, есть решение для max со статической проверкой типа, но мы можемне выражайте max в форме, отличной от CPS, 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());
}
Так что это не может быть проблемой для статической проверки типов, но это выглядит очень уродливо и не масштабируется далеко за пределыпростые примеры.Итак, чего не хватает в этом статическом языке, мы не можем предоставить статические и удобочитаемые решения.