Представление функторов высшего порядка в виде контейнеров в Coq - PullRequest
0 голосов
/ 25 февраля 2019

Следуя этому подходу, я пытаюсь смоделировать функциональные программы, используя обработчики эффектов в Coq, на основе реализации в Haskell.В статье представлены два подхода:

  • Синтаксис эффекта представлен в виде функтора и объединен со свободной монадой.
    data Prog sig a = Return a | Op (sig (Prog sig a))

Из-за проверки завершения неНравится не строго положительные определения, этот тип данных не может быть определен напрямую.Однако контейнеры могут использоваться для представления строго положительных функторов, как описано в этой статье .Этот подход работает, но так как мне нужно смоделировать эффект области действия, который требует явного синтаксиса области видимости, возможны несовпадающие теги начала / конца.Для рассуждений о программах это не идеально.

  • Во втором подходе используются функторы высшего порядка, т.е. следующий тип данных.
    data Prog sig a = Return a | Op (sig (Prog sig) a)

Теперь sig имееттип (* -> *) -> * -> *.Тип данных не может быть определен в Coq по тем же причинам, что и раньше.Я ищу способы для моделирования этого типа данных, чтобы я мог реализовать эффекты с областью без явных тегов области видимости.

Мои попытки определить контейнер для функторов более высокого порядка не были плодотворными, и я не могунайти что-нибудь об этой теме.Я благодарен за указатели в правильном направлении и полезные комментарии.

Редактировать: Одним из примеров синтаксиса с областью действия из статьи, которую я хотел бы представить, является следующий тип данных для исключений.

data HExc e m a = Throw′ e | forall x. Catch′ (m x) (e -> m x) (x -> m a)

Edit2: я объединил предложенную идею с моим подходом.

Inductive Ext Shape (Pos : Shape -> Type -> Type -> Type) (F : Type -> Type) A :=
  ext : forall s, (forall X, Pos s A X -> F X) -> Ext Shape Pos F A.

Class HContainer (H : (Type -> Type) -> Type -> Type):=
  {
    Shape   : Type;
    Pos     : Shape -> Type -> Type -> Type;
    to      : forall M A, Ext Shape Pos M A -> H M A;
    from    : forall M A, H M A -> Ext Shape Pos M A;
    to_from : forall M A (fx : H M A), @to M A (@from M A fx) = fx;
    from_to : forall M A (e : Ext Shape Pos M A), @from M A (@to M A e) = e
  }.

Section Free.
  Variable H : (Type -> Type) -> Type -> Type.

  Inductive Free (HC__F : HContainer H) A :=
  | pure : A -> Free HC__F A
  | impure : Ext Shape Pos (Free HC__F) A -> Free HC__F A.
End Free.

Код можно найти здесь .Пример лямбда-исчисления работает, и я могу доказать, что контейнерное представление изоморфно типу данных.Я попытался сделать то же самое для упрощенной версии типа данных обработчика исключений, но он не соответствует представлению контейнера.

Определение интеллектуального конструктора также не работает.В Haskell конструктор работает, применяя Catch' к программе, где может возникнуть исключение, и к продолжению, которое в начале пустое.

catch :: (HExc <: sig) => Prog sig a -> Prog sig a
catch p = inject (Catch' p return)

Основная проблема, которую я вижу в реализации Coq, заключается в том, чтоформа должна быть параметризована через функтор, что приводит к всевозможным проблемам.

Ответы [ 2 ]

0 голосов
/ 26 февраля 2019

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

Простые рекурсивные типы

Давайте сначала рассмотрим простой рекурсивный тип, чтобы понять непараметрические контейнеры и для сравнения с параметризованным обобщением.Лямбда-исчисление, не заботясь об объемах, дается следующим функтором:

Inductive LC_F (t : Type) : Type :=
| App : t -> t -> LC_F t
| Lam : t -> LC_F t
.

Из этого типа мы можем извлечь две информации:

  • shape сообщает нам о конструкторах (App, Lam) и, возможно, также вспомогательных данных, не относящихся к рекурсивной природе синтаксиса (здесь нет).Существует два конструктора, поэтому форма имеет два значения.Shape := App_S | Lam_S (bool также работает, но объявление форм в качестве автономных индуктивных типов обходится дешево, а именованные конструкторы также удваиваются в качестве документации.)

  • Для каждой фигуры (то есть конструктора), позиция говорит нам о рекурсивных вхождениях синтаксиса в этом конструкторе.App содержит две подтермы, поэтому мы можем определить их две позиции как логические;Lam содержит одну подтерму, поэтому его позиция является единицей.Можно также сделать Pos (s : Shape) индексированным индуктивным типом, но программировать с ним очень сложно (просто попробуйте).

(* Lambda calculus *)
Inductive ShapeLC :=
| App_S    (* The shape   App _ _ *)
| Lam_S    (* The shape   Lam _ *)
.

Definition PosLC s :=
  match s with
  | App_S => bool
  | Lam_S => unit
  end.

Параметризованные рекурсивные типы

Сейчаслямбда-исчисление с правильной областью действия:

Inductive LC_F (f : Type -> Type) (a : Type) : Type :=
| App : f a -> f a -> LC_F a
| Lam : f (unit + a) -> LC_F a
.

В этом случае мы все еще можем повторно использовать данные Shape и Pos из ранее.Но этот функтор кодирует еще одну часть информации: как каждая позиция изменяет параметр типа a.Я называю этот параметр контекстом (Ctx).

Definition CtxLC (s : ShapeLC) : PosLC s -> Type -> Type :=
  match s with
  | App_S => fun _ a => a  (* subterms of App reuse the same context *)
  | Lam_S => fun _ a => unit + a  (* Lam introduces one variable in the context of its subterm *)
  end.

Этот контейнер (ShapeLC, PosLC, CtxLC) связан с функтором LC_F изоморфизмом: между сигмой { s : ShapeLC & forall p : PosLC s, f (CtxLC s p a) } и LC_F a.В частности, обратите внимание, как функция y : forall p, f (CtxLC s p) говорит вам, как именно заполнить форму s = App_S или s = Lam_S, чтобы построить значение App (y true) (y false) : LC_F a или Lam (y tt) : LC_F a.

Мое предыдущее представление закодировано Ctxв некоторых дополнительных типовых индексах Pos.Представления эквивалентны, но это выглядит здесь более аккуратно.

Исчисление обработчика исключений

Мы рассмотрим только конструктор Catch.Он имеет четыре поля: тип X, основное вычисление (которое возвращает X), обработчик исключений (который также восстанавливает X) и продолжение (потребляющее X).

Inductive Exc_F (E : Type) (F : Type -> Type) (A : Type) :=
| ccatch : forall X, F X -> (E -> F X) -> (X -> F A) -> Exc_F E F A.

Форма представляет собой один конструктор, но вы должны включить X.По сути, посмотрите на все поля (возможно, разворачивая вложенные индуктивные типы) и сохраните все данные, которые не включают F, это ваша форма.

Inductive ShapeExc :=
| ccatch_S (X : Type)     (* The shape   ccatch X _ (fun e => _) (fun x => _) *)
.
(* equivalently, Definition ShapeExc := Type. *)

Тип позиции перечисляет все способыполучить F из Exc_F соответствующей формы.В частности, позиция содержит аргументы для применения функций и, возможно, любые данные для разрешения ветвления любого другого вида.В частности, вам нужно знать тип исключения для хранения исключений для обработчика.

Inductive PosExc (E : Type) (s : ShapeExc) : Type :=
| main_pos                      (* F X *)
| handle_pos (e : E)            (* E -> F X *)
| continue_pos (x : getX s)     (* X -> F A *)
.

(* The function getX takes the type X contained in a ShapeExc value, by pattern-matching: getX (ccatch_S X) := X. *)

Наконец, для каждой позиции вам нужно решить, как меняется контекст, т.е.X или A:

Definition Ctx (E : Type) (s : ShapeExc) (p : PosExc E s) : Type -> Type :=
  match p with
  | main_pos | handle_pos _ => fun _ => getX s
  | continue_pos _ => fun A => A
  end.

Используя условные обозначения ваш код , вы можете затем закодировать конструктор Catch следующим образом:

Definition Catch' {E X A}
           (m : Free (C__Exc E) X)
           (h : E -> Free (C__Exc E) X)
           (k : X -> Free (C__Exc E) A) : Free (C__Exc E) A :=
  impure (@ext (C__Exc E) (Free (C__Exc E)) A (ccatch_S X) (fun p =>
    match p with
    | main_pos => m
    | handle_pos e => h e
    | continue_pos x => k x
    end)).

(* I had problems with type inference for some reason, hence @ext is explicitly applied *)

Полная суть https://gist.github.com/Lysxia/6e7fb880c14207eda5fc6a5c06ef3522

0 голосов
/ 25 февраля 2019

Основной трюк в кодировании свободной монады «первого порядка» заключается в кодировании функтора F : Type -> Type в качестве контейнера, который по существу является зависимой парой { Shape : Type ; Pos : Shape -> Type }, так что для всех a тип F a изоморфно сигма-типу { s : Shape & Pos s -> a }.

. Продолжая эту идею, мы можем закодировать функтор высшего порядка F : (Type -> Type) -> (Type -> Type) как контейнер { Shape : Type & Pos : Shape -> Type -> (Type -> Type) }, так что для всех f иa, F f a изоморфно { s : Shape & forall x : Type, Pos s a x -> f x }.

Я не совсем понимаю, что там делает дополнительный параметр Type в Pos, но он работает ™, по крайней мере, дляотметим, что вы можете построить некоторые термины лямбда-исчисления в результирующем типе.

Например, функтор синтаксиса лямбда-исчисления:

Inductive LC_F (f : Type -> Type) (a : Type) : Type :=
| App : f a -> f a -> LC_F a
| Lam : f (unit + a) -> LC_F a
.

представлен контейнером (Shape, Pos), определенным как:

(* LC container *)

Shape : Type := bool; (* Two values in bool = two constructors in LC_F *)
Pos (b : bool) : Type -> (Type -> Type) :=
         match b with
         | true => App_F
         | false => Lam_F
         end;

, где App_F и Lam_F задаются как:

Inductive App_F (a : Type) : TyCon :=
| App_ (b : bool) : App_F a a
.

Inductive Lam_F (a : Type) : TyCon :=
| Lam_ : Lam_F a (unit + a)
.

Тогда свободноподобная монада Prog (неявно параметризованная (Shape, Pos)) определяется как:

Inductive Prog (a : Type) : Type :=
| Ret : a -> Prog a
| Op (s : Shape) : (forall b, Pos s a b -> Prog b) -> Prog a
.

Определив шаблон, вы можете написать следующий пример:

(* \f x -> f x x *)
Definition omega {a} : LC a :=
  Lam (* f *) (Lam (* x *)
    (let f := Ret (inr (inl tt)) in
     let x := Ret (inl tt) in
     App (App f x) x)).

Полный смысл: https://gist.github.com/Lysxia/5485709c4594b836113736626070f488

...