Поліморфізм та індуктивні типи даних


10

Мені цікаво. Я працюю над цим типом даних в OCaml :

type 'a exptree =
  | Epsilon
  | Delta of 'a exptree * 'a exptree
  | Omicron of 'a
  | Iota of 'a exptree exptree

З якими можна маніпулювати за допомогою явно набраних рекурсивних функцій (функція, яка була додана зовсім недавно). Приклад:

let rec map : 'a 'b. ('a -> 'b) -> 'a exptree -> 'b exptree =
  fun f ->
    begin function
    | Epsilon -> Epsilon
    | Delta (t1, t2) -> Delta (map f t1, map f t2)
    | Omicron t -> Omicron (f t)
    | Iota tt -> Iota (map (map f) tt)
    end

Але мені ніколи не вдалося визначити це в Coq :

Inductive exptree a :=
  | epsilon : exptree a
  | delta : exptree a -> exptree a -> exptree a
  | omicron : a -> exptree a
  | iota : exptree (exptree a) -> exptree a
.

Кок скуголить. Це не подобається останньому конструктору, і говорить щось, з чим я зовсім не розумію чи не згоден:

Error: Non strictly positive occurrence of "exptree" in "exptree (exptree a) -> exptree a".

Я можу зрозуміти, що індуктивні типи, що використовують заперечення всередині свого визначення, як type 'a term = Constructor ('a term -> …)відкидаються, тому що вони призведуть до некрасивих не обґрунтованих звірів, таких як (нетипізовані) λ-терміни. Однак цей конкретний exptreeтип даних видається досить нешкідливим: дивлячись на його визначення OCaml , його аргумент 'aніколи не використовується в негативних позиціях.

Здається, що Кок тут обережний. То чи справді є проблема з цим конкретним індуктивним типом даних? Або Кок може бути тут трохи дозвольнішим?

Крім того, що з іншими асистентами доказування, чи здатні вони впоратися з таким індуктивним визначенням (природним чином)?

Відповіді:


9

Це кілька разів з’являлось у списку розсилки Coq, але я ніколи не бачив переконливої ​​відповіді. Кок не такий загальний, як міг би бути; правила (Coquand, 1990) та (Giménez, 1998) (та його кандидатська дисертація) більш загальні і не потребують суворої позитивності. Однак позитиву недостатньо, коли ви виходите на вулицю Set; Цей приклад вийшов у кількох дискусіях :

Inductive Big : Type := B : ((B -> Prop) -> Prop) -> Big.

З простими структурами даних, такими як ваша, індуктивний тип не викликав би інших проблем, крім того, щоб ускладнити реалізацію.

Існує загальний спосіб визначити такі типи, як цей, визначений як точку фіксації многочлена:

F=ϵ+δ(F×F)+οid+FF

Замість того, щоб намагатися визначити функцію , визначте сімейство типів . Це означає додавання цілого параметра до типу, що кодує кількість самокомпозицій ( , , тощо) та додатковий конструктор інжекцій, щоб перетворити на .exptree:aexptree(a)exptree,exptreeexptree,exptreeexptreeexptree,exptree0(a)=aexptree1(a)=exptree(a)a e x p t r e e 0 ( a ) = aexptree2(a)=exptree(exptree(a))aexptree0(a)=a

Inductive et : nat -> Type -> Type :=
  | alpha : forall a, a -> et 0 a                      (*injection*)
  | omicron : forall n a, et n a -> et (S n) a         (**)
  | epsilon : forall (S n) a, et (S n) a
  | delta : forall n a, et (S n) a -> et (S n) a -> et (S n) a
  | iota : forall n a, et (S (S n)) a -> et (S n) a
.

Ви можете перейти до визначення значень та роботи над ними. Кок часто зможе зробити висновок про показник. Set Implicit Argumentsзробить ці визначення гарнішими.

Definition exptree := et 1.
Definition et1 : exptree nat :=
  delta _ _ (omicron _ _ (alpha _ 42)) (epsilon _ _).
Definition et2 : exptree nat := iota _ _ (omicron _ _ et1).

Ви можете вважати за краще зменшити аргумент на 1, так що exptreeце et 0. Це вилучає багато S nу визначенні, що може полегшити деякі докази, але це вимагає розділення початкового випадку від випадку рецидиву на кожен конструктор, який бере аргумент (замість того, щоб додати один конструктор інжекції для ). У цьому прикладі є лише один конструктор, який потрібно розділити, тому це має бути хорошим вибором.aaa

Inductive et : nat -> Type -> Type :=
  | omicron_0 : forall a, a -> et 0 a
  | omicron_S : forall n a, et n a -> et (S n) a
  | epsilon : forall n a, et n a
  | delta : forall n a, et n a -> et n a -> et n a
  | iota : forall n a, et (S n) a -> et n a
.
Definition exptree := et 0.
Definition et1 : exptree nat :=
  delta _ _ (omicron_0 _ 42) (epsilon _ _).
Definition et2 : exptree nat :=
  (iota _ _ (omicron_S _ _ et1)).

Я думаю, що це той самий принцип, запропонований Ральфом Маттесом у більш загальній формі .

Список літератури

Тьєррі Кокенд і Крістін Полін. Індуктивно визначені типи . У матеріалах COLOG'88 , LNCS 417, 1990. [ Springer ] [ Google ]

Едуардо Гіменес. Структурно-рекурсивні визначення в теорії типів . В ICALP'98: Матеріали 25-го Міжнародного колоквіуму з питань автоматів, мов та програмування. Спрингер-Верлаг, 1998. [ PDF ]


8

Ральф Меттс описує, як моделювати подібні типи у Coq у "Структурі даних для ітераційних сил" ( код , папір ).


6

Одне з перших, що робить Кок - це побудова принципу індукції, пов'язаного з індуктивним типом, який ви тільки що визначили, і розуміння основного принципу індукції - це хороша вправа.

Наприклад, O : nat | S : nat -> natбуде генеруватися принцип індукції P O -> (∀ n, P n -> P (S n)) -> ∀ n, P n.

Якому би відповідав принцип індукції iota? Здається , що немає ніякого предиката , Pякий був би в змозі говорити про P tта P (iota t), так як він повинен говорити про exptree a, exptree (exptree a), exptree (exptree (exptree a))...

Також Omicronробить те ж саме, але тип кожного разу менший. Ви повинні відчувати, що посилання на менший тип та на більший тип зробить речі безладними. (Це сказано, Omicronце правильний шлях)

Це не точний критерій, який говорить про те, чому це визначення не слід приймати, але це пояснює, чому він вважає себе неправильним.

exptreeздається, ви будуєте граматику для виразів, що, як правило, не таке рекурсивне. Ви хочете допомогти з цим?

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.