164 15.8. Дополнительные замечания
Интуиция, лежащая в основе этого правила, такова: если мы знаем, что терм имеет функциональные
типы S T
1
и S T
2
, то мы, безусловно, можем передать в эту фнукцию терм типа S и ожидать в
качестве результата как T
1
, так и T
2
.
Мощность понятия типов-пересечений можно продемонстрировать утверждением, что в варианте
простого типизированного лямбда-исчисления с наследованием, типами-пересечениями и вызовом по
имени, множество нетипизированных лямбда-термов, которым можно присвоить типы, в точности сов-
падает с множеством нормализуемых термов — т. е., терм типизируем тогда и только тогда, когда его
вычисление завершается! Отсюда немедленно следует, что проблема реконструкции типов (см. Гла-
ву 22) для исчисления с типами-перечислениями неразешима.
С более прагматической точки зрения, ценность типов-пересечений состоит в том, что они поддержи-
вают некоторую форму конечной перегрузки операторов. Например, мы можем присвоить тип (Nat
Nat Nat) (Float Float Float) операции сложения, которая применима как к натураль-
ным числам, так и к числам с плавающей точкой (например, используя теговые биты в представлении
аргументов, чтобы во время выполнения выбрать правильную машинную команду).
К сожадению, мощность типов-пересечений ведет к некоторым прагматическим трудностям для
разработчиков языков. Пока что лишь один полноразмерный язык, Forsythe (Reynolds 1988) включа-
ет типы-пересечения в их наиболее общем виде. Ограниченная форма, называемая типы-уточнения,
может оказаться более доступной (Freeman and Pfenning 1991; Pfenning 1993b; Davies 1997).
Двойственное понятие типов-объединений также оказывается весьа полезным. В отличие от типов-
сумм и вариантов (которые также иногда называют «объединениями», что приводит к путанице), T
1
T
2
обозначает обыкновенное объединение множества значений, принадлежащих к типу T
1
, и множе-
ства значений, принадлежащих к типу T
2
, без всякого тега, указывающего источник данного элемента.
Таким образом, Nat Nat — всего лишь псевдоним Nat. Неразъединенные типы-объединения уже дав-
но играли важную роль при анализе программ (Palsberg and Pavlopolou 1998), но редко встречались в
языках программирования (важное исключение — Algol 68, см. (van Wijngaarden et al. 1975)). Впрочем,
в последнее время их все чаще применяют в контексте систем типов для обработки «полуструктури-
рованных» форматов баз данных вроде XML (Buneman and Pierce 1998; Hosoya, Vouillon and Pierce
2001).
Основное формальное различие между разъединенными (неперекрывающимися) и неразъединен-
ными типами-пересечениями состоит в том, что для последних отсутствует какой-либо аналог кон-
струкции case: если известно, что значение v имеет тип T
1
T
2
, то мы можем безопасно применять к
нему только те операции, которые имеют смысл как для T
1
, так и для T
2
. (Например, если T
1
и T
2
—
записи, имеет смысл обращаться к их общим полям.) Бестеговый тип union в языке C служит источ-
ником нарушения типовой безопасности именно потому, что в языке игнорируется это ограничение, и
над элементом типа T
1
T
2
разрешена любая операция, имеющая смысл либо для T
1
, либо для T
2
.
15.8. Дополнительные замечания
Идея наследования в языках программирования восходит к 1960-м годам, к Симуле (Birtwistle, Dahl,
Myhrhaug and Nygaard 1979) и ее родственникам. Первые попытки формального анализа принадлежат
Рейнольдсу (Reynolds 1980) и Карделли (Cardelli 1984).
Правила типизации и, особенно, наследования, работающие с записями, несколько сложнее, чем те,
что мы видели до сих пор, поскольку в них либо переменное число предпосылок (по одной на поле), либо
исполльзуются дополнительные механизмы вроде перестановок индексов полей. Существует мноество
других способов записи для этих правил, однако все они либо столь же сложны, либо избегают слож-
ности, полагаясь на неформальные соглашения (напр., многоточие: «l
1
:T
1
...l
n
:T
n
»). Недовольство
таким положением дел привело Карделли и Митчелла к разработке исчисления операций над запи-
сями (Cardelli and Mitchell 1991), где макрооперация создания записи с многими полями выражается
через базовое значение-пустую запись и операцию добавления одного поля за раз. В рамках этой точ-
ки зрения можно также рассмотреть дополнительные операции, вроде изменения значения поля или
конкатенации записей (Harper and Pierce 1991). Правила типизации для этих операций оказываются
достаточно тонкими, особенно при наличии параметрического полиморфизма, так что большинство
разработчиков предпочитают работать с обыкновенными записями. Тем не менее, система Карделли и
rev. 104