Ответ 1
Типы, параметризованные значениями, называются зависимыми типами.1 Было проведено много исследований по теме зависимых типов, но мало он достиг "основного языка".
Большая проблема с зависимыми типами заключается в том, что если ваши типы содержат выражения, т.е. биты кода, тогда средство проверки типов должно иметь возможность выполнять код. Это невозможно сделать в полной общности: что, если код имеет побочные эффекты? что, если код содержит бесконечный цикл? Например, рассмотрите следующую программу в синтаксисе типа C (ошибка проверки опущена):
int a, b;
scanf("%d %d", &a, &b);
int u[a], v[b];
Как компилятор мог узнать, имеют ли массивы u
и v
одинаковые размеры? Это зависит от номеров, которые вводит пользователь! Одним из решений является запрещение побочных эффектов в выражениях, которые появляются в типах. Но это не заботится обо всем:
int f(int x) { while (1); }
int u[f(a)], v[f(b)];
компилятор перейдет в бесконечный цикл, пытаясь решить, имеют ли теги u
и v
одинаковые размеры.
< расширен >
Таким образом, запрещайте побочные эффекты внутри типов и ограничивайте рекурсию и цикл для доказуемо завершающих случаев. Это делает проверку типов разрешимой? С теоретической точки зрения, да, это возможно. То, что у вас есть, может быть чем-то вроде Coq proof term. Проблема в том, что проверка типов тогда легко разрешима, если у вас достаточно аннотаций типа (аннотации типов - это информация для ввода, которую предоставляет программист). И здесь достаточно много значит. Очень много. Как и в, аннотации типов на каждой отдельной конструкции языка, а не только объявления переменных, а также вызовы функций, операторы и все остальное. И типы будут представлять 99,9999% от размера программы. Часто было бы быстрее написать все на С++ и отладить его, а не написать всю программу со всеми требуемыми аннотациями типа.
Следовательно, трудность здесь состоит в том, чтобы иметь систему типов, которая требует только разумного количества аннотаций типа. С теоретической точки зрения, как только вы позволяете отказаться от некоторых аннотаций типа, она становится проблемой типа вывода, а не чистым типом проверка проблемы. И вывод типа неразрешимый для даже относительно простых систем типа. Вы не можете легко получить разрешимую (гарантированную прекращение) статическую (работающую во время компиляции) разумную (не требующую безумного количества аннотаций типа) зависимую систему типов.
<Суб > </& расширенная GT;суб >
Зависимые типы иногда появляются в ограниченной форме на основных языках. Например, C99 позволяет массивы, размер которых не является постоянным выражением; тип такого массива является зависимым типом. Неудивительно, что для C компилятор не обязан проверять границы на таком массиве, даже если потребуется проверить границы для массива постоянного размера.
Более полезно, Dependent ML является диалектом ML с типами, которые могут быть проиндексированы с помощью простых целочисленных выражений. Это позволяет проверке типа статически проверять большинство границ массива.
В модульных системах для ML показан другой пример зависимого типа. Модули и их подписи (также называемые интерфейсами) похожи на выражения и типы, но вместо описания вычислений они описывают структуру программы.
Зависимые типы часто возникают на языках, которые не являются языками программирования, в смысле большинства программистов, но скорее языки для доказательства математических свойств программ (или просто математических теорем). Большинство примеров на странице wikipedia имеют такую природу.
¹ В более общем плане теоретики типа классифицируют системы типов в зависимости от того, имеют ли они типы более высокого порядка (типы, параметризованные по типам) polymorphism (выражения, параметризованные типами), и зависимые типы (типы, параметризованные выражениями). Эта классификация называется куб Barendregt или лямбда-куб. На самом деле это гиперкуб, но обычно четвертое измерение (выражения, параметризованные выражениями, т.е. Функциями) само собой разумеется.