Из этой ссылки: Строгий позитив
Строгое условие позитивности исключает такие объявления, как
data Bad : Set where
bad : (Bad → Bad) → Bad
A B C
-- A is in a negative position, B and C are OK
Почему А отрицательный? Также почему Б разрешено? Я понимаю, почему C допускается.
A
в конечном итоге привести к бесконечному расширению и взрыву стека (в языках на основе стека).Ответы:
Сначала терминологическое объяснение: отрицательные и положительные позиции исходят из логики. Они об асимметрии в логических связок: в ведет себяразному от B . Похожая вещь происходит в теории категорий, где мы говоримконтравариантныйиковариантныйвместо отрицательного и положительного соответственно. В физике они говорят о величинах, которые ведут себя «ковариантно» и «тоже контравариантно». Так что это очень общее явление. Программист может думать о них как о «входе» и «выходе».A ⇒ B A В
Теперь на индуктивные типы данных.
Подумайте индуктивный тип данных в качестве своего рода алгебраической структуры: конструкторы операции , которые принимают элементы T в качестве аргументов и производят новые элементы T . Это очень похоже на обычную алгебру: сложение берет два числа и производит число.T T T
В алгебре принято, что операция принимает конечное число аргументов, и в большинстве случаев она принимает ноль (постоянный), один (унарный) или два (двоичных) аргумента. Это удобно обобщить для конструкторов типов данных. Предположим
c
, это конструктор для типа данныхT
:c
это константа, мы можем думать о ней как о функцииunit -> T
, или эквивалентно(empty -> T) -> T
,c
унарный, мы можем думать о нем как о функцииT -> T
или, что то же самое(unit -> T) -> T
,c
бинарный, мы можем думать о нем как о функцииT -> T -> T
, или эквивалентноT * T -> T
, или эквивалентно(bool -> T) -> T
,c
который принимает семь аргументов, мы могли бы рассматривать его как функцию,(seven -> T) -> T
гдеseven
есть некоторый ранее определенный тип с семью элементами.c
который принимает бесконечно много аргументов, это будет функция(nat -> T) -> T
.Эти примеры показывают, что общая форма конструктора должна быть
куда мы звоним
A
на Арность оc
и мы думаем,c
как конструктор , который принимаетA
-many аргументов типаT
для получения элементаT
.Здесь есть кое-что очень важное: арность должна быть определена до того, как мы ее определим
T
, иначе мы не можем сказать, что должны делать конструкторы. Если кто-то пытается получить конструктортогда вопрос "сколько аргументов
broken
нужно?" не имеет хорошего ответа. Вы можете попытаться ответить на негоT
словами «требуется множество аргументов», но это не сработает, посколькуT
еще не определено. Мы могли бы попытаться выбраться из кунундрума, используя причудливую теорию с фиксированной точкой, чтобы найти типT
и инъективную функцию(T -> T) -> T
, и добились бы успеха, но мы также нарушили бы принцип индукции наT
этом пути. Так что это просто плохая идея попробовать такую вещь.Ради полноты позвольте мне объяснить всю историю. Нам нужно немного обобщить приведенную выше форму конструкторов. Иногда у нас есть операции или конструкторы, которые принимают параметры . Например, для скалярного умножения требуется скаляр и вектор v, чтобы получить вектор λ ⋅ v . Это одинарная операция над векторами, параметризованная скаляром. Мы могли быλ v λ ⋅ v смотреть скалярное умножение бесконечно много одинарных операций, по одному для каждого скаляра, но это раздражает. Итак, общая форма конструктора
c
должна позволять параметр некоторого типаB
:Действительно, многие конструкторы можно переписать таким образом, но не все, нам нужно еще один шаг, а именно мы должны позволить ,
A
чтобы зависеть отB
:Это окончательная форма конструктора для индуктивного типа. Это также точно, что W-типы. Форма настолько общая, что нам нужен только один конструктор
c
! Действительно, если у нас есть два из нихтогда мы можем объединить их в один
где
Кстати, если мы карри общего вида, мы видим, что это эквивалентно
что ближе к тому, что люди на самом деле записывают в виде помощников. Помощники по проверке позволяют нам записывать конструкторы удобным способом, но они эквивалентны общей форме выше (упражнение!).
источник
Первое вхождение
Bad
называется «отрицательным», потому что оно представляет аргумент функции, т. Е. Находится слева от стрелки функции (см. « Рекурсивные типы бесплатно» от Филиппа Вадлера). Я предполагаю, что происхождение термина «отрицательная позиция» происходит от понятия контравариантности («контра» означает противоположное).Не допускается, чтобы тип определялся в отрицательной позиции, потому что можно писать не завершающие программы, используя его, то есть сильная нормализация терпит неудачу в его присутствии (подробнее об этом ниже). Кстати, это и есть причина названия правила «строгая позитивность»: мы не допускаем отрицательных позиций.
Мы допускаем второе вхождение,
Bad
поскольку оно не вызывает не-завершение, и мы хотим использовать определяемый тип (Bad
) в некоторой точке рекурсивного типа данных ( перед последней стрелкой его конструктора).Важно понимать, что следующее определение не нарушает строгое правило позитивности.
Правило применяется только к аргументам конструктора (которые
Good
в данном случае оба ), а не к самому конструктору (см. Также « Сертифицированное программирование с зависимыми типами » Адама Члипалы ).Еще один пример нарушения строгой позитивности:
Вы также можете проверить этот ответ на отрицательные позиции.
Подробнее о недопустимости ... Страница, на которую вы ссылаетесь, содержит некоторые пояснения (наряду с примером на Хаскеле):
Вот аналогичный пример в Ocaml, который показывает, как реализовать рекурсивное поведение без (!) Непосредственного использования рекурсии:
В
nonTerminating
функции «распаковывается» функция от аргумента и яблок с оригиналом аргумента. Здесь важно то, что большинство систем типов не разрешают передавать функции самим себе, поэтому такой термин, как «f f
проверка типов», не будет проверяться, поскольку не существует типа,f
удовлетворяющего проверке типов. Одной из причин, по которой были введены системы типов, является отключение самостоятельного применения (см. Здесь ).Обтекание типов данных, подобных представленному выше, можно использовать для обхода этого препятствия на пути к несогласованности.
Я хочу добавить, что бесконечные вычисления вносят несоответствия в логические системы. В случае Agda и Coq
False
индуктивный тип данных не имеет конструкторов, поэтому вы никогда не сможете создать проверочный термин типа False. Но если бы разрешались не завершающиеся вычисления, это можно сделать, например, так (в Coq):Тогда
loop 0
будет проверка типовloop 0 : False
, так что при переписке Карри-Говарда это будет означать, что мы доказали ложное предположение.Upshot : строгое правило положительности для индуктивных определений предотвращает неразрывные вычисления, которые губительны для логики.
источник