Weak Static and Strong Dynamic typing
Сравнение языков программирования - задача нетривиальная. Многие сравнивают их в разрезе синтаксических конструкций, базовых библиотек, современности, популярности, производительности и т.д. При этом полезной нагрузки от таких процедур минимум - каждый хвалит свое корыто.
Типизация языков программирования - одна из немногих основополагающих концепций, которыми языки отличаются. Она влияет на все составляющие части - от семантики до компиляторов и средств разработки.
На данный момент различают статическую, динамическую, строгую и нестрогую типизицию языков. В этой статье я попытаюсь обьяснить свое виденье этих понятий.
О типах и типизации
Прежде чем разговаривать о типизации и ее видах необходимо разобраться с самими понятиями типа и типизации.
Тип - это некая информация о участке памяти.
Это определение можно счесть нечетким и слишком низкоуровневым, но, в конечном итоге код на любом языке превращается в байты и выполняется вентилями. Можно возразить, что типы больше асоциируются с переменными, но это не всегда так. Я хочу свести типизацию языков программирования к одному уровню, с которым будет проще всего оперировать.
Как ни крути, а екземпляр любого типа T
представляет собой кусок памяти размером sizeof(T)
или что-то в этом духе. Задача компилятора состоит в трансляции конструкций языка с определенной семантикой в
последовательность инструкций процессора.
Типизацией назовем совокупность правил, которыми обязан руководствоваться компилятор при трансляции операций соответствующего языка над типами данных в машинный код.
Строгая (strong) и нестрогая (weak) типизация
Мне кажется, что эти два противоположных понятия следует объединить в одно - строгость типизации. Рассмотрим похожие фрагменты кода различных языков:
// PHP - говорят, что это язык с нестрогой типизацией $a = "1"; $b = 2; $c = a + b; // $c == 3
// C# 3.0 - говорят, что это язык со строгой типизацией String a = "1"; Int32 b = 2; var c = a + b; // $c == "12"
Что же я вижу здесь? Первое: переменные могут быть связаны с типом данных (C# 3.0) или не связаны (PHP). Это влияет на возможность определить тип результата или легитимность операций во время обработки исходного кода (компилятором или средой разработки). Многие утверждают, что именно это указывает на строгую или нестрогую типизацию, а я считаю, что привязанность переменных к типам языка напрямую связана с разделением на статическую и динамическую типизацию. Второе: компилятор имеет некие правила преобразования конструкций, которые вызывают нестыковку с системой типов языка или представлением разработчика о их логичности. Чем больше этих правил - тем меньше строгость типизации. Насколько мне известно, все языки имеют поддержку минимум одного неявного преобразования типов (coercion), поэтому языка с 100%-но строгой типизацией не существует.
Статическая (static) и динамическая (dynamic) типизация
Это уже намного интереснее и более важно. Я склонен относить язык к статически типизированным, если информация о типах операндов известна на этапе компиляции, и типизированным динамически, если утверждение не верно.
Статическая типизация (static typing)
Рассмотрим пример на языке С++:
class CppClass { public: void SomeOperation() {} }; void PerformOperation(CppClass &c) { c.SomeOperation(); } ... CppClass c; PerformOperation(c);
Компилятор С++ знает все, что ему требуется для трансляции этого кода в машинные инструкции - адрес переменной
c
, ее тип - CppClass
, наличие метода SomeOperation
и его точный адрес в памяти.
Информация о типах используется компилятором и, как правило, не добавляется в выходной модуль.
Статическая типизация не запрещает компилятору встраивать информацию о типах:
// C++ с включенной поддержкой RTTI DerivedCppClass derived; BaseCppClass &base = derived; ... DerivedCppClass &derived = dynamic_cast<DerivedCppClass&>(base);
Пользы от RTTI в С++ практически нет, поскольку единственное, что можно узнать о типе во время выполнения, - это его
строковое имя, а наличие в коде операторов dynamic_cast
свидетельствует о плохом понимании программистом
принципов ООП.
В управляемых языках, таких как Java или C#, подробная информация о типе зашивается в выходной модуль и доступна для использования во время выполнения программы. Этот механизм называется интроспекцией (reflection) и имеет свою довольно высокую цену в виде многократной потери производительности.
Динамическая типизация (dynamic typing)
Python - хороший пример динамически типизированного языка:
class C(object): def hello(): print 'Hello!' def x(c): c.hello # WTF?! c = C() x(c)
Итак, информация о типах на этапе компиляции неизвестна. Компилятор спокойно проглатывает даже самый бредовый, но синтаксически корректный код. При этом строгая проверка типов делается, но во время выполнения программы. Компилятор, естественно, добавляет такую возможность. Правильно, путем встраивания информации о типах! Потеря производительности при этом может даже превышать таковую в управляемых языках.
Преимущества и недостатки статической (static) и динамической (dynamic) типизаций
Статическая (static) типизация
Преимущества:
- Легче ориентироваться в коде
- Хорошая поддержка со стороны средств разработки (рефакторинг, подсказки)
- Скорость выполнения
- Скорость выполнения
- Скорость выполнения
Недостатки:
- Усложнение языка (и, как последствие, увеличение стоимости разработки и поддержки)
- Реализация полиморфизма через наследование
- Ложное впечатление о корректности кода (
компилируется - значит работает
) - И еще
Динамическая (dynamic) типизация
Преимущества:
- Хорошая поддержка современных стандартов и технологий (SOA, например)
- Выразительность и простота кода
- Скорость разработки
- И еще
Недостатки:
- Низкая производительность
- Необходимость качественного и обширного тестирования
Дополнительные ссылки по теме:
0 коммент.:
Отправить комментарий