Реализация преобразования SKI - доказать, что возвращаемое значение имеет обещанный тип - PullRequest
0 голосов
/ 07 ноября 2019

Я пытаюсь реализовать функцию extract, которая принимает выражение типа (f (g x y)) вместе с переменной, например y, и создает функцию y --> (f (g x y)) с комбинаторами SKI. В этом случае результат должен быть (S (K f) (g x)).

В каком-то смысле я делаю преобразование из лямбда-термина в его версию SKI.

I 'Я пытаюсь сделать типизированную версию этого, и у меня трудные времена.


Настройка

Типы в этих выражениях представлены следующим индуктивным типом

Inductive type : Type :=
| base_type    : forall (n : nat), type
| arrow_type   : type -> type -> type.

По сути, у меня есть несколько базовых типов, проиндексированных целыми числами (base_type), а также я могу создавать типы функций между ними (arrow_type)

Ввести нотацию для типов функций

Notation "A --> B" := (arrow_type A B) (at level 30, right associativity).

Выражения представлены следующим индуктивным типом

Inductive term : type -> Type :=

| var    : forall (n : nat) (A : type), term A
| eval   : forall {A B : type}, term (A-->B) -> term A -> term B

| I      : forall (A : type)    , term (A --> A)
| K      : forall (A B : type)  , term (A --> (B --> A))
| S      : forall (A X Y : type), term ((A --> X --> Y) --> (A --> X) --> A --> Y).

Здесь я снова имею набор базовых переменных, индексированных целыми числами n : nat и типом A : type (не Type!)

Таким образом, переменная x : term X является выражением с type X.

Чтобы уменьшить болевые ощущения, давайте введем нотацию для оценки функции

Notation "f [ x ]" := (eval f x) (at level 25, left associativity).

Вводный пример

Исходный вопрос можно сформулировать более точно следующим образом.

Давайте начнем с определения с некоторых типов

Notation X := (base_type 0).
Notation Y := (base_type 1).

Определение переменных x y и функций f g (все они могут быть проиндексированы с 0, поскольку все они имеют разные type)

Notation x := (var 0 X).
Notation y := (var 0 Y).
Notation g := (var 0 (X --> Y --> X)).
Notation f := (var 0 (X --> Y)).

Полученное выражение type равно Y.

Check f[g[x][y]].

Моя цель - создать функцию extract такую, что

extract f[g[x][y]] y

производит

S[K[f]][g[x]]

с заполненным типом

(S Y X Y)[(K (X-->Y) Y)[f]][g[x]]

Равенство по type и term

Для продолжения определения extract Мне нужно определить равенство для type и term.

Require Import Arith.EqNat.
Open Scope bool_scope.

Fixpoint eq_type (A B : type) : bool :=
  match A, B with
  | base_type n,    base_type m      => beq_nat n m
  | arrow_type X Y, arrow_type X' Y' => (eq_type X X') && (eq_type Y Y')
  | _, _  => false                                                      
  end.

Fixpoint eq_term {A B : type} (a : term A) (b : term B) : bool :=
  match a, b with
  | var n X      , var n' X'        => (beq_nat n n') && (eq_type X X')
  | eval X Y f x , eval X' Y' f' x' => (eq_type X X') && (eq_type Y Y') && (eq_term f f') && (eq_term x x')
  | I  X         , I X'             => (eq_type X X')
  | K X Y        , K X' Y'          => (eq_type X X') && (eq_type Y Y')
  | S Z X Y      , S Z' X' Y'       => (eq_type X X') && (eq_type Y Y') && (eq_type Z Z')
  | _            , _                => false                                   
  end.

Попытка реализации extract

«Реализация» довольно проста

Fixpoint extract {A B : type} (expr : term B) (val : term A) : term (A-->B) :=
  if (eq_term expr val)
  then (I A)
  else 
    match expr with
    | eval X Y f x => (S A X Y)[extract f val][extract x val]
    | _            => (K B A)[expr]
    end.

Есть две проблемы

  1. При возврате I A: type из I A равно A --> A не A --> B, как обещано, но в этом конкретном случае я должен бытьвозможность доказать, что B и A одинаковы.
  2. При возврате (S A X Y)[...: возвращаемое значение равно A --> Y, а не A --> B, но опять же я должен бытьспособен доказать, что Y равно B.

Как я могу доказать B=A и Y=B в тех конкретных случаях, когда определение функции принимается?

Ответы [ 2 ]

1 голос
/ 07 ноября 2019

Что вы можете сделать, это превратить eq_type и eq_term из логических функций в процедуры принятия решений о равенстве. На данный момент, насколько я могу судить, ваше равенство полностью синтаксическое. Таким образом, вы можете просто использовать понятие равенства Coq, чтобы говорить о равенстве терминов и типов. Затем вы можете написать:

Definition eq_type_dec (A B : type) : { A = B } + { A <> B }. 

Вы в значительной степени сопоставляете шаблон с A и B, затем возвращаете left eq_refl для случаев равенства и right ... в других случаяхгде ... - все, что вам нужно сделать, чтобы доказать неравенство.

Сделайте то же самое и определите eq_term_dec. Здесь у вас есть два варианта: сделать равенство типов внутренним или внешним:

Definition eq_term_dec (A B : type) (a : A) (b : B) :
  { (A = B) * (existT (fun t => t) A a = existT (fun t => t) B b) }
  +
  { (A <> B) + (existT (fun t => t) A a <> existT (fun t => t) B b) }. 

или:

Definition eq_term_dec (A : type) (a b : term A) : { a = b } + { a <> b }. 

Первый вариант кажется довольно ужасным для написания, но дает вам больше гибкости,Вероятно, я бы предпочел последнее и использовал бы его под eq_type_check при работе с возможными неравными типами.

Как только вы их получите, вы можете превратить ваш if в зависимого match:

Fixpoint extract {A B : type} (expr : term B) (val : term A) : term (A-->B) :=
  match eq_type_dec A B with
  | left eqAB =>
    match eqAB
          in eq _ B1
          return term B1 -> term (A --> B1)
    with
    | eq_refl => fun expr1 => (* now expr1 : A *)
      match eq_expr_dec _ _ expr1 val with
      | left eqab => I A
      | right neqab => (* ... *)
      end
    end expr (* note here we pass the values that must change type *)
  | right neqAB => (* ... *)
  end.

В ветвях, которые я выбрал, еще может быть немало работы. Вы могли бы взглянуть на различные способы выполнения такого зависимого программирования, либо вручную, как я показал здесь, либо используя зависимую тактику исключения, либо используя рекурсоры этих типов.


EDIT

Чтобы ответить на ваш комментарий, вот два способа написания eq_term_dec, которые я знаю. Одним из способов является использование Program расширения Coq, которое добавляет одну аксиому и становится намного более способным работать с зависимыми типами:

Require Import Program.Equality.

Fixpoint eq_term_dec (A : type) (a b : term A) : { a = b } + { a <> b }.
  dependent induction a; dependent induction b; try (right ; congruence).
  - destruct (PeanoNat.Nat.eq_dec n n0); [ left | right ]; congruence.

Другой способ - фактически определить термин с зависимой типизациейтебе нужно. Должен быть способ сделать это, используя тактику, но я не совсем уверен, как поступить, однако я знаю, как написать термин. Это не для слабонервных, и я не ожидаю, что вы поймете, что происходит, пока вы не ознакомитесь с зависимым сопоставлением с образцом и «образцом конвоя». Вот если вы хотите увидеть, как это выглядит:


Fixpoint eq_term_dec (A : type) (a b : term A) : { a = b } + { a <> b }.
  revert b.
  destruct a.
  {
    destruct b; try (right ; congruence).
    destruct (PeanoNat.Nat.eq_dec n n0) ; [ left | right ] ; congruence.
  }
  { destruct b; admit. (* skipping this, it's easy *) }
  {
    (* Here is the complication:                                                  *)
    (* `b` has type `term (A -> A)`                                               *)
    (* If you abstract over its type, the goal is ill-typed, because the equality *)
    (* `I A = b` is at type `A -> A`.                                             *)
    intros b.

    refine (
        (fun (T : type) (ia : term T) (b : term T) =>
           match b
                 as b1
                 in term T1
                 return forall (ia0 : term T1),
                          match T1 as T2 return term T2 -> term T2 -> Type with
                          | arrow_type Foo Bar => fun ia1 b2 => {ia1 = b2} + {ia1 <> b2}
                          | _ => fun _ _ => True
                          end ia0 b1
           with
           | var n a  => fun b => _
           | eval h a => fun b => _
           | I A      => fun b => _
           | K A B    => fun b => _
           | S A B C  => fun b => _
           end ia
        ) (A --> A) (I A) b
      ).

    (* from now on it's easy to proceed *)
    destruct a.
    easy.
    destruct b; try ( right ; congruence ).
    destruct (PeanoNat.Nat.eq_dec n n0) ; [ left | right ] ; congruence.

    (* one more to show it's easy *)
    destruct t0.
    easy.
    destruct b; try ( right ; congruence ).

    (* etc... *)

0 голосов
/ 15 ноября 2019

У меня есть решение, оно не красивое, но, кажется, работает. Особенно, доказательство eq_term_dec очень длинное и безобразное.

Если кому-то интересно, мое решение:

Inductive type : Type :=
| base_type    : forall (n : nat), type
| arrow_type   : type -> type -> type.

Notation "A --> B" := (arrow_type A B) (at level 30, right associativity).
Inductive term : type -> Type :=

| var    : forall (n : nat) (A : type), term A
| eval   : forall {A B : type}, term (A-->B) -> term A -> term B

| I      : forall {A : type}    , term (A --> A)
| K      : forall {A B : type}  , term (A --> (B --> A))
| S      : forall {A X Y : type}, term ((A --> X --> Y) --> ((A --> X) --> (A --> Y))).

(* Coercion term : type >-> Sortclass. *)

Notation "n :: A" := (var n A).
Notation "f [ x ]" := (eval f x) (at level 25, left associativity).

Fixpoint eq_type_dec (A B : type) : {A = B} + {A <> B}.
Proof.
  decide equality.
  decide equality.
Defined.

Require Import Coq.Logic.Eqdep.

Fixpoint eq_term_dec {A B : type} (a : term A) (b : term B) :
  ( (A = B) * (existT (fun T : type => term T) A a = existT (fun T : type => term T) B b) )
  +
  ( (A <> B) + (existT (fun T : type => term T) A a <> existT (fun T : type => term T) B b) ).
Proof.
  case a as [n X| X Y f x | X | X Y | Z X Y], b as [n' X'| X' Y' f' x' | X' | X' Y' | Z' X' Y'].

  (* var n X ? var n' X'*)
  - assert (ndec : {n=n'} + {n<>n'}) by decide equality.
    pose (Xdec := eq_type_dec X X').

    destruct ndec as [eqn | neqn], Xdec as [eqX | neqX].
    left.
    rewrite eqn.
    rewrite eqX.
    split; reflexivity.

    right; left.  apply neqX.
    right; right. 
    intro H; inversion H as [H1]. auto.
    right; left. apply neqX.

  - right; right; intro H; inversion H. (* n ?  f[x] *)
  - right; right; intro H; inversion H. (* n ? I *)
  - right; right; intro H; inversion H. (* n ? K *)
  - right; right; intro H; inversion H. (* n ? S *)
  - right; right; intro H; inversion H. (* f[x] ? n *)

  - pose (xdec := eq_term_dec _ _ x x').
    pose (fdec := eq_term_dec _ _ f f').

    destruct xdec, fdec.

    (* x = x' && f = f' *)
    left.
    split.
    apply fst in p0.
    inversion p0.
    auto.

    apply snd in p0.
    inversion p0.

    revert dependent x.
    revert dependent f.
    rewrite H0.
    rewrite H1.
    intros.
    apply snd in p.
    assert (x=x'). apply inj_pair2; apply p.
    assert (f=f'). apply inj_pair2; apply p0.
    rewrite H, H3. auto.

    right.
    destruct s.
    left. intro.
    apply fst in p.
    assert (X-->Y = X' --> Y').
    rewrite H, p.
    auto. auto.

    right. intro.
    inversion H.
    apply n.
    revert dependent x.
    revert dependent f.
    rewrite H1.
    rewrite H2.
    intros.
    apply inj_pair2 in H4.
    apply inj_pair2 in H4.
    rewrite H4.
    auto.

    right.
    destruct s.
    inversion p.
    inversion H.
    auto.
    inversion p.
    inversion H0.
    revert dependent x.
    revert dependent f.
    rewrite H2.
    rewrite H3.
    intros.
    apply inj_pair2 in H0.
    rewrite H0.
    right.
    intro.
    apply inj_pair2 in H1.
    inversion H1. auto.

    destruct s, s0.
    right. right.
    intro. inversion H. auto.
    right. right.
    intro. inversion H. auto.
    right. right.
    intro. inversion H. auto.
    right. right.
    intro. inversion H. auto.

  - right; right; intro H; inversion H. (* f[x] ? I *)
  - right; right; intro H; inversion H. (* f[x] ? K *)
  - right; right; intro H; inversion H. (* f[x] ? S *)
  - right; right; intro H; inversion H. (* I ? n *)
  - right; right; intro H; inversion H. (* I ? f[x] *)

  - pose (Xdec := eq_type_dec X X'). (* I ? I *)

    destruct Xdec.

    left; split; rewrite e; auto.
    right; left. intro. inversion H. auto.

  - right; right; intro H; inversion H. (* I ? K *)
  - right; right; intro H; inversion H. (* I ? S *)
  - right; right; intro H; inversion H. (* K ? n *)
  - right; right; intro H; inversion H. (* K ? f[x] *)
  - right; right; intro H; inversion H. (* K ? I *)

  - pose (Xdec := eq_type_dec X X').
    pose (Ydec := eq_type_dec Y Y').

    destruct Xdec, Ydec.

    left; split; rewrite e; rewrite e0; auto.
    right; left; intro; inversion H; auto.
    right; left; intro; inversion H; auto.
    right; left; intro; inversion H; auto.

  - right; right; intro H; inversion H. (* K ? S *)
  - right; right; intro H; inversion H. (* S ? n *)
  - right; right; intro H; inversion H. (* S ? f[x] *)
  - right; right; intro H; inversion H. (* S ? I *)
  - right; right; intro H; inversion H. (* S ? K *)

  - pose (Xdec := eq_type_dec X X').
    pose (Ydec := eq_type_dec Y Y').
    pose (Zdec := eq_type_dec Z Z').

    destruct Xdec, Ydec, Zdec.

    left; split; rewrite e; rewrite e0; rewrite e1; auto.
    right; left; intro; inversion H; auto.
    right; left; intro; inversion H; auto.
    right; left; intro; inversion H; auto.
    right; left; intro; inversion H; auto.
    right; left; intro; inversion H; auto.
    right; left; intro; inversion H; auto.
    right; left; intro; inversion H; auto.
Defined.

Fixpoint extract {A B : type} (expr : term B) (val : term A) : term (A-->B).
Proof.
  pose (ab_dec := eq_term_dec expr val).
  destruct ab_dec.

  (* expr is equal to val *)
  apply fst in p; rewrite p; apply I.

  (* expr is not equal to val *)
  inversion expr as [n X | X Y f x | X | X Y | Z X Y].

  (* expr is just a constant, i.e. expr = var n X *)
  apply (K[expr]).

  (* expr is a function evaluation, i.e. expr = f[x]*)
  apply (S[extract _ _ f val][extract _ _ x val]).

  (* expr is identity, i.e. expr = I *)
  rewrite H; apply (K[expr]).

  (* expr is constant function, i.e. expr = K *)
  rewrite H; apply (K[expr]).

  (* expr is constant function, i.e. expr = S *)
  rewrite H; apply (K[expr]).
Defined.

Notation X := (base_type 0).
Notation Y := (base_type 1).

Notation x := (var 0 X).
Notation y := (var 0 Y).
Notation f := (var 0 (X --> Y --> X)).

Compute extract (f[x]) x.    (* => S [K [f]] [I] *)
Compute extract (f[x][y]) x. (* => S [S [K [f]] [I]] [K [y]] *)
...