356 30.5. Идем дальше: зависимые типы
Заметим, что в каждом из трех вызовов cons первый аргумент имеет разное значение, что отражает
различную длину аргументов-списков.
В информатике и логике имеется общирная литература по зависимым типам. Хорошими отправ-
ными точками могут служить работы Смита, Нордстрема и Петерсона (Smith, Nordstr¨om and Peterson
1990), Томпсона (Thompson 1991), Луо (Luo 1994) и Хофмана (Hofmann 1997).
Упражнение 30.5.1 : То, что тип элементов списка жестко задан как Float, упрощает воспри-
ятие примера. Однако нетрудно обобщить его до списков произвольного типа T при помощи обыкно-
венных операторов над типами. Покажите, как это сделать.
Продолжая в том же духе, можно построить функции высшего порядка для работы со списками с
подобным же образом уточненными типами. Например, можно написать функцию сортировки, чей тип
sort : Πn : Nat . Fl o atList n -> Fl oatList n
говорит нам, что она возвращает список той же длины, что получает на входе. Более того, путем даль-
нейшего уточнения используемых семейств типов можно даже написать функцию sort так, чтобы ее
тип говорил, что возвращаемый список всегда отсортирован. Проверка, что функция sort принадлежит
этому типу, в сущности, окажется доказательством, что функция соответствует своей спецификации!
Такие примеры рисуют соблазнительную картину мира, где программы верны по построению, где
тип программы говорит нам все, что требуется знать о ее поведении, и одобрение от процедуры проверки
типов вселяет полную уверенность, что программа ведет себя как ожидается. Такое видение связано
с идеей программирования через «извлечение вычислительного содержимого» из доказательства, что
спецификацию возможно удовлетворить. Ключевое наблюдение состоит в том, что конструктивное
доказательство теоремы вида «Для каждого x существует y, такое, что P » можно рассматривать как
функцию, переводящую x в y, и снабженную информацией (вычислительно несущественной — т. е.,
представляющей интерес только для процедуры проверки типов), что эта функция обладает свойством
P . Эти идеи исследовались в рамках проектов Nuprl (Constable et al. 1986), LEGO (Luo and Pollack
1992; Pollack 1994), Coq (Paulin-Mohring 1989) и некоторых других.
К сожалению, мощность зависимых типов — обоюдоострое оружие. Размывание различий меж-
ду проверкой типов и доказательством произвольных теорем не приводит к волшебному облегчению
процесса доказательств — напротив, оно превращает проверку типов в вычислительно неразрешимую
задачу! Работа математика с программами механического содействия доказательству не состоит в том,
чтобы просто записать условие теоремы, нажать кнопку и ждать, пока программа ответит «Да» или
«Нет»: требуются значительные усилия по написанию сценариев доказательств и тактик, помогаю-
щих инструменту построить и верифицировать доказательство. Если идея правильности по построению
будет доведена до логического конца, можно ожидать, что программисты будут тратить сопоставимые
усилия по аннотации программ подсказками и объяснениями, помогающими программе проверки ти-
пов. Для некоторых критических программистских задач такие усилия могут быть оправданы, но для
каждодневного программирования они почти наверняка чрезмерны.
Тем не менее, было несколько попыток ввести зависимые типы в проекты практических языков про-
граммирования, в том числе Russell (Donahue and Demers 1985; Hook 1984), Cayenne (Augustsson 1998),
Зависимый ML (Xi and Pfenning 1998, 1999), Язык Ассемблера с Зависимыми Типами (Xi and Harper
2001), а также типы форм Джея и Секанины (Jay and Sekanina). В этих языках имеется тенденция
ограничивать мощность зависимых типов различными способами. В результате получаются системы,
более управляемые с вычислительной точки зрения, и для них легче автоматизировать проверку типов.
Например, в языках Си и др. зависимые типы используются только для уничтожения проверок выхода
за границы массива во время выполнения; в этих языках задачи «доказательства теорем», возникаю-
щие при проверке типов, представляют собой всего лишь системы линейных ограничений, и для них
существуют хорошие автоматизированные методы решения.
Одна из областей, где зависимые типы давно влияют на языки программирования — разработка си-
стем модулей, включающая механизмы, отслеживающие разделение межмодульных зависимостей. В
этой области классическими являются Pebble (Burstall and Lampson 1984), работы Маккуина (MacQueen
1986), Митчелла и Харпера (Mitchell and Harper 1988), Харпера и др. (Harper et al. 1990) и Харпера
и Стоуна (Harper and Stone 2000). В последнее время стал использоваться технический механизм од-
ноэлементных видов, при котором зависимость между модулями отслеживается на уровне видов, а не
типов (напр., Stone and Harper 2000; Crary 2000; см. также Hayashi 1991; Aspinall 1994).
rev. 104