Теория языков программирования

Статическая vs динамическая типизация

2019 год. Airbnb переводит весь frontend на TypeScript. Анализ тысяч production-багов показывает: 38% из них были бы пойманы статической типизацией ещё до запуска. Каждый третий баг, который долетал до пользователей в 3 часа ночи - это TypeError или undefined-доступ, который компилятор TypeScript отловил бы за секунды. Статика vs динамика - это не вопрос вкуса. Это инженерный trade-off с измеримыми последствиями.

  • TypeScript vs JavaScript - почему 78% JS-проектов переходят на TS: compile-time safety на масштабе
  • Python type hints - зачем динамическому языку добавили статические типы: mypy ловит баги без смены рантайма
  • Rust vs C - как строгая типизация предотвращает уязвимости безопасности (use-after-free, buffer overflow)
  • Выбор языка для стартапа: скорость разработки vs надёжность в production - реальный trade-off

Как Python получил типы - история одного компромисса

2014 год. Guido van Rossum, создатель Python, лично пишет PEP 484 - Python type hints. Казалось бы странно: человек, который 25 лет строил динамический язык, добавляет в него статические аннотации. Объяснение простое: Dropbox, где работал Guido, написал mypy - статический анализатор Python-кода. В крупных кодовых базах (100K+ строк) динамика превращалась в nightmare: рефакторинг - рулетка, автодополнение в IDE - угадывание. Type hints не меняют рантайм Python - они дают инструментам (mypy, pyright, IDE) возможность статически анализировать код. Gradual typing победил pragmatically.

Предварительные знания

  • What Is a Type System

Статическая типизация

**Статическая типизация** - типы всех выражений известны и проверяются **до запуска** программы, на этапе компиляции. Компилятор читает код и проверяет совместимость типов. Это не строгость ради строгости - это инструмент, который работает за разработчика 24/7, даже в ветках кода, до которых тесты никогда не доберутся.

Статический type checker - как корректор, который читает рукопись до публикации. Находит целый класс ошибок ещё до того, как программа запустится. В 2019 году Airbnb перешли с JavaScript на TypeScript и обнаружили: **38% багов** из production были бы пойманы статической типизацией ещё на этапе компиляции.

**Языки со статической типизацией:** Java, C, C++, Rust, Go, Haskell, Kotlin, Swift, TypeScript. Все они проверяют типы до запуска - но с разной степенью строгости.

ПреимуществоКак это работает
Раннее обнаружение баговКомпилятор ловит ошибки типов до запуска - не нужно ждать runtime
IDE supportАвтодополнение, рефакторинг, go-to-definition - IDE знает типы всех переменных
Документация в кодеСигнатуры функций показывают, что входит и что выходит
ПроизводительностьКомпилятор оптимизирует код, зная типы заранее - нет runtime-проверок
Безопасный рефакторингПереименовал поле? Компилятор покажет ВСЕ места, где оно используется

Когда статический типизатор обнаруживает ошибку типов?

Динамическая типизация

**Динамическая типизация** - типы проверяются **во время выполнения** программы. Переменная не привязана к конкретному типу: сейчас число, через строку - строка. Язык проверяет совместимость типов только когда операция реально выполняется. Это открывает возможность для быстрого прототипирования - но создаёт класс ошибок, которые прячутся до самого неудобного момента.

Гибкость динамических языков - палка о двух концах. Быстрое прототипирование: не нужно объявлять типы, код короче, итерации быстрее. Но цена - **ошибки, которые статический язык поймал бы сразу, выстреливают в самый неподходящий момент**.

«А тесты?» Тесты помогают, но покрытие **никогда не бывает 100%**. Статическая типизация проверяет ВСЕ пути выполнения, тесты - только те, которые написаны. Один пропущенный edge case - и TypeError в продакшене.

  • Быстрое прототипирование — Нет аннотаций типов, код короче. Идеально для скриптов, прототипов, data science. Python-скрипт на 10 строк - без церемоний.
  • Хрупкость в production — TypeError, AttributeError, undefined is not a function - классика динамических языков. Баги прячутся в редко выполняемых ветках кода.

**Языки с динамической типизацией:** Python, JavaScript, Ruby, PHP, Lua, Perl, Elixir, Clojure. Все они проверяют типы только при выполнении операций.

Какой код вызовет ошибку только в runtime, но не при компиляции?

Компромиссы и матрица типизации

Одно из самых распространённых заблуждений: **Static = Strong, Dynamic = Weak**. Это не так. «Статическая/динамическая» и «сильная/слабая» - это **две независимые оси**. Python динамический - но сильный. C статический - но слабый. JavaScript динамический и слабый. Путать их - значит неправильно выбирать инструмент для задачи.

**Сильная типизация** - язык не выполняет неявные преобразования между несовместимыми типами. **Слабая типизация** - язык тихо конвертирует типы, даже когда это приводит к абсурдным результатам.

Сильная (Strong)Слабая (Weak)
Статическая (Static)Java, Rust, Haskell, KotlinC, C++
Динамическая (Dynamic)Python, Ruby, ElixirJavaScript, PHP, Perl
КритерийСтатическаяДинамическая
Скорость разработкиМедленнее на старте (аннотации типов)Быстрее прототипирование
НадёжностьБаги ловятся при компиляцииБаги проявляются в runtime
ПроизводительностьОптимизация на этапе компиляцииOverhead на runtime-проверки типов
РефакторингБезопасный: компилятор проверит всёРискованный: без тестов не понять, что сломалось
IDE supportОтличный: автодополнение, навигацияОграниченный: IDE «угадывает» типы
Кривая обученияВыше: нужно понимать систему типовНиже: просто пишешь код

Gradual typing - компромисс эпохи

2012 год. Anders Hejlsberg (автор Delphi, C#, Turbo Pascal) выпускает TypeScript. Идея проста: начинать без типов, добавлять постепенно в критичные места. 2014 год - Python 3.5 получает type hints (PEP 484), Guido van Rossum лично пишет первый черновик. К 2024 году TypeScript используют более 78% JavaScript-проектов на GitHub. Gradual typing победил не потому что «правильный» - а потому что реальный.

Статическая типизация = сильная, динамическая = слабая

Это две независимые оси. Python - динамическая + сильная, C - статическая + слабая

«Когда проверяются типы» (static/dynamic) и «насколько строго» (strong/weak) - разные вопросы. JavaScript слабый не потому что динамический, а потому что делает безумные неявные преобразования.

Python - это какая типизация?

Утиная типизация и структурная типизация

If it walks like a duck and quacks like a duck, then it is a duck.

**Утиная типизация (duck typing)** - тип объекта не важен. Важно, **что он умеет делать**. Если объект имеет метод `quack()` - можно вызвать `quack()`, независимо от того, утка это или робот. Именно так работает Python `len()`: он не проверяет тип - он проверяет наличие `__len__`. Любой объект с `__len__` - «достаточно список».

А что, если нужен duck typing, но **со статической проверкой**? Тут появляется **структурная типизация**: компилятор проверяет не *имя* типа, а его *структуру* - какие поля и методы он имеет. TypeScript и Go используют именно этот подход - и это делает их одновременно гибкими и безопасными.

ПодходЯзыкПроверкаКогда
Duck typingPython, RubyНаличие методов у объектаВ runtime
Structural typingTypeScript, GoСовпадение структуры типовПри компиляции
Nominal typingJava, C#, RustЯвное объявление implements/traitsПри компиляции

**Структурная типизация** - это «статический duck typing». Гибкость утиной типизации (не нужно явно объявлять интерфейсы) вместе с безопасностью статической проверки (ошибки ловятся до запуска).

Duck typing и динамическая типизация - одно и то же

Duck typing - конкретный подход к проверке типов по поведению. Его статический аналог - structural typing (TypeScript, Go)

Динамическая типизация - КОГДА проверяются типы (в runtime). Duck typing - КАК проверяются (по наличию методов). Go доказывает, что duck typing возможен в статическом языке через structural typing.

Какой язык использует структурную типизацию (structural typing)?

Ключевые идеи

  • **Статическая типизация** проверяет типы до запуска (compile time) - ранние баги, IDE support, безопасный рефакторинг
  • **Динамическая типизация** проверяет типы в runtime - быстрое прототипирование, но TypeError в продакшене
  • **Strong/Weak != Static/Dynamic** - это две независимые оси. Python - динамический + сильный, C - статический + слабый
  • **Duck typing** - проверка по поведению, не по имени типа. Structural typing (TypeScript, Go) - его статический аналог

Связанные темы

Статическая vs динамическая типизация - центральная ось теории языков программирования:

  • Системы типов — Фундамент: что такое типы и зачем они нужны
  • Вывод типов (Type Inference) — Как статические языки избавляются от явных аннотаций - лучшее из двух миров
  • Gradual Typing — TypeScript, Python hints - постепенное добавление типов в динамический язык

Вопросы для размышления

  • Если бы в стартапе начинали с нуля - выбрали бы статический или динамический язык? Как изменился бы ответ через 2 года, когда кодовая база вырастет в 10 раз?
  • Почему Python добавил type hints, а не перешёл на статическую типизацию? Что бы потерялось?
  • Можно ли сказать, что structural typing (TypeScript) - это «идеальный компромисс» между duck typing и nominal typing? Или у него есть свои минусы?

Связанные уроки

  • plt-02-type-systems — Фундамент типов необходим перед сравнением статической и динамической проверки
  • plt-04-type-inference — Вывод типов - способ иметь статику без explicit аннотаций
  • plt-05-gradual — Gradual typing - TypeScript и Python hints как практический компромисс
  • comp-18-type-checking — Type checker в компиляторе реализует именно статическую проверку
  • se-01 — Выбор языка для проекта - прямое применение знаний о типизации
  • plt-08-generics — Generics - продвинутая static typing feature, строящаяся на этих основах
Статическая vs динамическая типизация

0

1

Войти