Учебник Теория типов для начинающих
Глубокий курс по теории типов для продвинутых студентов и функциональных программистов. От парадокса Рассела и нетипизированного лямбда-исчисления — к простому типизированному лямбда-исчислению, выводу типов Хиндли — Милнера, полиморфизму, алгебраическим типам и соответствию Карри — Говарда, а затем к зависимым типам, системам типов реальных языков и связи с теорией категорий. Лямбда-интерпретатор, type checker и алгоритм унификации реализованы на чистом stdlib Python и исполняются прямо в браузере.
Курс «Теория типов» состоит из 8 разделов и 28 уроков: Что такое теория типов и зачем она, История и мотивация: парадоксы, Нетипизированное лямбда-исчисление, Простое типизированное лямбда-исчисление, Вывод типов: Хиндли — Милнер, Полиморфизм и алгебраические типы, Карри — Говард и классы типов и Зависимые типы, реальные языки и горизонты. Уроки идут по порядку — от основ к более сложным темам, в каждом есть объяснение с примерами, а в конце — вопросы для самопроверки. К урокам привязаны задачи с автоматической проверкой: прочитали тему — сразу закрепили её кодом.
Программа курса
1 Что такое теория типов и зачем она
- Что такое тип на самом деле
Тип — не «целое» или «строка», а множество значений с операциями и обещание, которое компилятор проверяет до запуска программы.
- Типы как спецификация и предотвращение ошибок
Сигнатура функции — это контракт. Чем выразительнее тип, тем больше неверных программ он отвергает ещё до запуска.
- Типы как фундамент языков и доказательств
Системы типов лежат в основе компиляторов, IDE и формальной верификации: один и тот же аппарат проверяет программы и доказательства.
- Что такое тип на самом деле
2 История и мотивация: парадоксы
- Парадокс Рассела и кризис оснований
Парадокс Рассела о «множестве всех множеств, не содержащих себя» пошатнул основания математики и породил теорию типов как лекарство.
- Типы как лекарство от парадоксов
Теория типов Рассела ввела иерархию уровней: объект всегда ниже коллекции, а самоприменение запрещено — парадокс исчезает.
- Краткая история систем типов
От Чёрча и STLC через Хиндли — Милнера, System F и Карри — Говарда к зависимым типам и языкам вроде Rust и Coq.
- Парадокс Рассела и кризис оснований
3 Нетипизированное лямбда-исчисление
- Синтаксис лямбда-исчисления
Три конструкции — переменная, абстракция, аппликация — порождают полную модель вычислений. Разбираем грамматику и приоритеты.
- Связанные и свободные переменные, альфа-эквивалентность
Имя переменной — лишь ярлык: связанные имена можно переименовывать (альфа-эквивалентность), а свободные несут смысл из контекста.
- Бета-редукция: вычисление как переписывание
Единственное правило вычисления: подставить аргумент в тело функции. Реализуем безопасную подстановку и нормализацию на Python.
- Лямбда как модель вычислений: числа Чёрча
Без чисел, булевых и циклов — только функции. Кодируем натуральные числа и сложение, исполняем интерпретатор и получаем 2+3=5.
- Синтаксис лямбда-исчисления
4 Простое типизированное лямбда-исчисление
- Зачем типизировать лямбда-исчисление
Нетипизированное исчисление допускает бессмысленные и незавершающиеся термы. Типы отсекают их и гарантируют завершаемость STLC.
- Типы-функции и контекст типизации
Стрелочный тип A -> B и контекст Г связывают переменные с типами. Учимся читать суждение Г |- e : T.
- Правила типизации STLC
Три правила — переменная, абстракция, аппликация — полностью задают типизацию STLC. Реализуем чекер на Python.
- Чтение дерева вывода типов
Дерево вывода (derivation) — формальное доказательство, что терм имеет тип. Учимся строить и читать его на ASCII.
- Зачем типизировать лямбда-исчисление
5 Вывод типов: Хиндли — Милнер
- Проверка против вывода типов
Type checking требует аннотаций и проверяет их; type inference выводит типы сам. В чём разница и почему вывод сложнее.
- Типовые переменные и унификация
Унификация решает уравнения между типами, находя подстановку переменных. Реализуем алгоритм с occurs-check на Python.
- Алгоритм W: вывод как в Haskell и OCaml
Algorithm W обходит терм, порождает уравнения и решает их унификацией. Собираем мини-движок вывода типов на Python.
- Let-полиморфизм и обобщение
Чтобы одна let-привязка работала на разных типах, её тип обобщают в схему forall. Так id применяется и к числу, и к строке.
- Проверка против вывода типов
6 Полиморфизм и алгебраические типы
- Параметрический полиморфизм и параметричность
Одна функция forall a. для всех типов сразу. Параметричность даёт «бесплатные теоремы»: по типу можно угадать поведение.
- Алгебраические типы: суммы и произведения
Произведения (кортежи, записи) и суммы (варианты) — два кирпича данных. Их «алгебра» считает число обитателей типа.
- Ad-hoc полиморфизм, перегрузка и субтипизация
Кроме параметрического есть ad-hoc полиморфизм (перегрузка, классы типов) и полиморфизм подтипов. Разбираем три вида и их различия.
- Алгебраические типы как логика
Произведение — конъюнкция, сумма — дизъюнкция, функция — импликация, Void — ложь. Первый взгляд на соответствие типов и логики.
- Параметрический полиморфизм и параметричность
7 Карри — Говард и классы типов
- Соответствие Карри — Говарда
Глубочайшая идея теории типов: типы — это утверждения, программы — доказательства, а проверка типов — проверка доказательства.
- Классы типов и словари
Type classes Haskell — дисциплинированная перегрузка. Под капотом они компилируются в передачу словарей методов. Моделируем механизм.
- Пропозиции как типы на практике: Coq и Lean
Как доказывают теоремы и верифицируют программы в Coq, Agda, Lean: тактики, проверенное ядро, реальные результаты вроде CompCert.
- Соответствие Карри — Говарда
8 Зависимые типы, реальные языки и горизонты
- Зависимые типы: типы, зависящие от значений
Vector длины n, where n — значение в типе. Зависимые типы позволяют доказывать свойства программ: Agda, Idris, Coq.
- Системы типов реальных языков и продвинутые возможности
Компромиссы Haskell, Rust, TypeScript, Scala и продвинутые механизмы: GADT, higher-kinded types, линейные типы, эффекты.
- Границы вывода, практическая польза и связь с категориями
Вывод vs проверка vs реконструкция, почему полный вывод бывает неразрешим, как типы улучшают любой код и обзор связи с теорией категорий.
- Зависимые типы: типы, зависящие от значений