Что является необдуманным примером того, что статическая проверка типов слишком консервативна?

9

В « Концепциях языков программирования» Джон Митчелл пишет, что статическая проверка типов обязательно является консервативной (чрезмерно строгой) из-за проблемы остановки. Он приводит в качестве примера:

if (complicated-expression-that-could-run-forever)
   then (expression-with-type-error)
   else (expression-with-type-error)

Может ли кто-то дать не надуманный ответ, который действительно будет иметь практическое значение?

Я понимаю, что Java позволяет динамически проверять приведение в таких случаях:

if (foo instanceof Person) {
    Person p = (Person) foo;
    :
}

но я считаю, что это скорее недостаток языка / компилятора Java, чем проблема межязыкового языка.

Эллен Спертус
источник
2
Пример Java, который вы привели, является не надуманным примером того, как статическая проверка типов слишком консервативна. Другими словами, ответ зависит от того, какую систему типов вы имеете в виду. Для любого примера, который мы придумаем, всегда будет система типов, которая может обработать этот пример (система типов не слишком консервативна в этом примере). Для любой системы типов мы всегда можем найти пример, где она слишком консервативна. Итак, я думаю, вам нужно указать систему типов. Если система типов Java - это не то, что вы имели в виду, есть ли что-то более конкретное, о чем вы думали? Вывод типа ML-стиля?
DW
Можно утверждать, что в качестве примера можно привести «консервативный» статический анализ кода , а не обязательную проверку типов. было бы полезно определить «консервативный» . возможно, все системы статического типа будут «консервативными» по сравнению с динамическими системами, потому что первые являются более строгими во время компиляции по определению. однако можно утверждать, что ни один из них не является более строгим во время выполнения, поскольку динамическая проверка может также возвращать похожие (основанные на типе) ошибки. и, между прочим, динамически проверяемые приведения во время исполнения в языках не являются недостатком , они есть в большинстве статически проверенных языков, возможно доказуемо нужных.
ВЗН

Ответы:

7

Я всегда рассматривал это скорее для удобства, чем для того, чтобы алгоритм мог или не может быть выражен вообще. Если бы я действительно хотел запускать программы, подобные придуманной Митчеллом, я просто написал бы соответствующий симулятор Turing Machine на своем статически типизированном языке.

Уловка со статической системой типов состоит в том, чтобы предложить правильные виды гибкости только для случаев, когда гибкость позволяет вам писать код, который легче поддерживать.

Вот несколько примеров методов структурирования программ, которые иногда считаются более простыми в динамическом управлении, чем в статически типизированных языках.

Дженерики и Контейнеры

В статически типизированных языках до ML (ок. 1973 г.) и CLU (ок. 1974 г.) было несложно создать красно-черное дерево строк, красно-черное дерево целых чисел, красно-черное дерево с плавающей точкой или красно-черное дерево элементов определенного типа Foo. Однако было трудно (возможно, невозможно) создать единственную реализацию красно-черного дерева, которая была бы статически проверена и могла обрабатывать любой из этих типов данных. Обойти эту проблему можно было следующим образом: (1) полностью вырваться из системы типов (например, используяvoid * в C), (2) написать себе какой-то макрос препроцессор, а затем написать макросы, которые производят код для каждого конкретного типа, который вы хотите, или (3) использовать подход Lisp / Smalltalk (и Java) для проверки типа извлеченного Объект динамически

ML и CLU ввели понятие, соответственно, предполагаемых и явно объявленных (статических) параметризованных типов, которые позволяют вам писать универсальные, статически типизированные, контейнерные типы.

Подтип Полиморфизм

В статически типизированных языках до Simula67 (ок. 1967 г.) и Hope (ок. 1977 г.) было невозможно одновременно выполнить динамическую диспетчеризацию и статически проверить, что вы охватили случай для каждого подтипа. Многие языки имели некоторую форму меченых союзов , но это было обязанностью программиста , чтобы убедиться , что их caseили switchзаявления, или их таблицы прыгать, покрыли каждый возможный тег.

Языки, следующие модели Simula (C ++, Java, C #, Eiffel), предоставляют абстрактные классы с подклассами, где компилятор может проверить, что каждый подкласс реализовал все методы, объявленные родительским классом. Языки, следующие модели Hope (все варианты ML, от SML / NJ до Haskell), имеют алгебраические подтипы, где компилятор может проверить, что каждый typecaseоператор охватывает все подтипы.

Patching от Monkey и Аспектно-ориентированное программирование

Системы динамического типа значительно упрощают различные методы создания прототипов. В языках, где типы представлены хеш-картами из строк в функции (например, Python, Javascript, Ruby), довольно легко глобально изменить поведение каждого модуля, который зависит от конкретного типа, просто путем динамического изменения хеш-карты, представляющей тип.

Хотя существуют очевидные способы, с помощью которых исправление обезьяны может усложнить поддержку программ, существуют также способы, с помощью которых они могут быть использованы для «добра», а не «зла». В частности, в аспектно-ориентированном программировании можно использовать методы, аналогичные мартышко-патчированию, для таких вещей, как изменение типа файла для указания на виртуализированную файловую систему, позволяющее создавать инфраструктуры для модульного тестирования для «бесплатных», или модифицировать простые типы исключений, чтобы они печатать сообщения журнала каждый раз, когда они перехвачены для лучшей отладки.

В отличие от Generics и Subtype Polymorphism, где ключевые идеи статической проверки были доступны в 1970-х годах, статическая проверка для аспектно-ориентированного программирования является (я думаю) активной областью исследований. Я мало что знаю об этом, за исключением того, что с 2001 года существует язык AspectJ .

Блуждающая логика
источник