Coq 平等实现

2023-12-23

我正在编写一种玩具语言,其中 AST 中的节点可以有任意数量的子节点(Num has 0, Arrow有 2 个,等等)。您可以致电这些接线员。此外,AST 中可能只有一个节点被“聚焦”。我们对数据类型进行索引Z如果它有焦点,或者H如果没有。

我需要有关代码的某些部分的建议。希望一次性询问所有这些问题是可以的,因为它们是相关的。

  1. 您将如何定义单一焦点的内部节点类型,InternalZ?现在我说“我们有S n孩子们 -n其中一个是不聚焦的,一个(在某个给定的索引处)是聚焦的。一个稍微更直观的选项(看起来像拉链)是InternalZ : forall n m, arityCode (n + 1 + m) -> Vector.t (t H) n -> t Z -> Vector.t (t H) m -> t Z。不过,我知道我不想处理这个附加内容。

  2. 精炼类型:在两种有趣的情况下eq我比较两者ns(孩子的数量)。如果它们是相同的,我应该能够“强制”arityCodes and Vector.ts 具有相同的类型。现在我用两个引理解决了这个问题。我应该如何正确地做到这一点?看起来像 Adam Chlipala 的“车队模式” http://adam.chlipala.net/cpdt/html/Cpdt.MoreDep.html可能有帮助,但我不知道如何做。

  3. 如果我取消注释其中任何一个Vector.eqb调用时,Coq 抱怨“无法猜测修复的递减参数。”。我理解这个错误,但我不知道如何规避它。我首先想到的是我可能需要索引t由其儿童的深度。

My code:

Module Typ.
  Import Coq.Arith.EqNat.
  Import Coq.Structures.Equalities.
  Import Coq.Arith.Peano_dec.
  Import Fin.
  Import Vector.

  (* h: unfocused, z: focused *)
  Inductive hz : Set := H | Z.

  (* how many children can these node types have *)
  Inductive arityCode : nat -> Type :=
    | Num   : arityCode 0
    | Hole  : arityCode 0
    (* | Cursor : arityCode 1 *)
    | Arrow : arityCode 2
    | Sum   : arityCode 2
    .

  Definition codeEq (n : nat) (l r : arityCode n) : bool :=
    match l, r with
      | Num, Num     => true
      | Hole, Hole   => true
      | Arrow, Arrow => true
      | Sum, Sum     => true
      | _, _         => false
    end.

  (* our AST *)
  Inductive t : hz -> Type :=
    | Leaf      : arityCode 0 -> t H
    | Cursor    : t H -> t Z
    | InternalH : forall n, arityCode n -> Vector.t (t H) n -> t H
    | InternalZ : forall n, arityCode (S n) -> Vector.t (t H) n -> Fin.t n * t Z -> t Z
    (* alternative formulation: *)
    (* | InternalZ : forall n m, arityCode (n + 1 + m) -> Vector.t (t H) n -> t Z -> Vector.t (t H) m -> t Z *)

    . 

  Lemma coerceArity (n1 n2 : nat) (pf : n1 = n2) (c1 : arityCode n1) : arityCode n2.
    exact (eq_rect n1 arityCode c1 n2 pf).
  Qed.

  Lemma coerceVec {A : Type} {n1 n2 : nat} (pf : n1 = n2) (c1 : Vector.t A n1) : Vector.t A n2.
    exact (eq_rect n1 (Vector.t A) c1 n2 pf).
  Qed.


  (* this is the tricky bit *)
  Fixpoint eq {h_or_z : hz} (ty1 ty2 : t h_or_z) : bool :=
    match ty1, ty2 with
    | Leaf c1, Leaf c2 => codeEq c1 c2
    | Cursor ty1, Cursor ty2 => eq ty1 ty2
    | InternalH n1 c1 ty1, InternalH n2 c2 ty2 =>
      match eq_nat_dec n1 n2 with
        | right _neqPrf => false
        | left eqPrf    => 
          let c1'  := coerceArity eqPrf c1 in
          let ty1' := coerceVec eqPrf ty1 in
          codeEq c1' c2 (* && Vector.eqb _ eq ty1' ty2 *)
        end
     | InternalZ n1 c1 v1 (l1, f1), InternalZ n2 c2 v2 (l2, f2) => 
       match eq_nat_dec n1 n2 with
       | right _neqPrf => false
       | left eqPrf    =>
         let eqPrf' := f_equal S eqPrf in
         let c1'    := coerceArity eqPrf' c1 in
         let v1'    := coerceVec eqPrf v1 in
         codeEq c1' c2 (* && Vector.eqb _ eq v1' v2 *) && Fin.eqb l1 l2 && eq f1 f2 
       end
    | _, _ => false
    end.
End Typ.

让我们从你的第三个问题开始。Vector.eqb对其第一个参数执行嵌套递归调用。为了让 Coq 相信这些正在减少,我们需要定义coerceVec透明,通过替换Qed with Defined:

Require Coq.Arith.EqNat.
Require Coq.Structures.Equalities.
Require Coq.Arith.Peano_dec.
Require Fin.
Require Vector.

Set Implicit Arguments.

Module Typ.
  Import Coq.Arith.EqNat.
  Import Coq.Structures.Equalities.
  Import Coq.Arith.Peano_dec.
  Import Fin.
  Import Vector.

  (* h: unfocused, z: focused *)
  Inductive hz : Set := H | Z.

  Inductive arityCode : nat -> Type :=
    | Num   : arityCode 0
    | Hole  : arityCode 0
    | Arrow : arityCode 2
    | Sum   : arityCode 2
    .

  Definition codeEq (n : nat) (l r : arityCode n) : bool :=
    match l, r with
      | Num, Num     => true
      | Hole, Hole   => true
      | Arrow, Arrow => true
      | Sum, Sum     => true
      | _, _         => false
    end.

  Inductive t : hz -> Type :=
    | Leaf      : arityCode 0 -> t H
    | Cursor    : t H -> t Z
    | InternalH : forall n, arityCode n -> Vector.t (t H) n -> t H
    | InternalZ : forall n, arityCode (S n) -> Vector.t (t H) n -> Fin.t n * t Z -> t Z
    .

  Lemma coerceArity (n1 n2 : nat) (pf : n1 = n2) (c1 : arityCode n1) : arityCode n2.
    exact (eq_rect n1 arityCode c1 n2 pf).
  Defined.

  Lemma coerceVec {A : Type} {n1 n2 : nat} (pf : n1 = n2) (c1 : Vector.t A n1) : Vector.t A n2.
    exact (eq_rect n1 (Vector.t A) c1 n2 pf).
  Defined.

  Fixpoint eq {h_or_z : hz} (ty1 ty2 : t h_or_z) : bool :=
    match ty1, ty2 with
    | Leaf c1, Leaf c2 => codeEq c1 c2
    | Cursor ty1, Cursor ty2 => eq ty1 ty2
    | @InternalH n1 c1 ty1, @InternalH n2 c2 ty2 =>
      match eq_nat_dec n1 n2 with
        | right _neqPrf => false
        | left eqPrf    =>
          let c1'  := coerceArity eqPrf c1 in
          let ty1' := coerceVec eqPrf ty1 in
          codeEq c1' c2 && Vector.eqb _ eq ty1' ty2
        end
     | @InternalZ n1 c1 v1 (l1, f1), @InternalZ n2 c2 v2 (l2, f2) =>
       match eq_nat_dec n1 n2 with
       | right _neqPrf => false
       | left eqPrf    =>
         let eqPrf' := f_equal S eqPrf in
         let c1'    := coerceArity eqPrf' c1 in
         let v1'    := coerceVec eqPrf v1 in
         codeEq c1' c2 && Vector.eqb _ eq v1' v2 && Fin.eqb l1 l2 && eq f1 f2
       end
    | _, _ => false
    end.
End Typ.

至于你的第二个问题:一般来说,你确实需要使用相等证明来实现强制转换操作,就像你所做的那样coerceVec。然而,在这种特殊情况下,更容易避免使用具有不同索引的元素的强制转换和编写比较函数:

Require Coq.Arith.EqNat.
Require Coq.Structures.Equalities.
Require Coq.Arith.Peano_dec.
Require Fin.
Require Vector.

Set Implicit Arguments.

Module Typ.
  Import Coq.Arith.EqNat.
  Import Coq.Structures.Equalities.
  Import Coq.Arith.Peano_dec.
  Import Fin.
  Import Vector.

  (* h: unfocused, z: focused *)
  Inductive hz : Set := H | Z.

  Inductive arityCode : nat -> Type :=
    | Num   : arityCode 0
    | Hole  : arityCode 0
    | Arrow : arityCode 2
    | Sum   : arityCode 2
    .

  Definition codeEq (n1 n2 : nat) (l : arityCode n1) (r : arityCode n2) : bool :=
    match l, r with
      | Num, Num     => true
      | Hole, Hole   => true
      | Arrow, Arrow => true
      | Sum, Sum     => true
      | _, _         => false
    end.

  Inductive t : hz -> Type :=
    | Leaf      : arityCode 0 -> t H
    | Cursor    : t H -> t Z
    | InternalH : forall n, arityCode n -> Vector.t (t H) n -> t H
    | InternalZ : forall n, arityCode (S n) -> Vector.t (t H) n -> Fin.t n * t Z -> t Z
    .

  Fixpoint eq {h_or_z : hz} (ty1 ty2 : t h_or_z) : bool :=
    match ty1, ty2 with
    | Leaf c1, Leaf c2 => codeEq c1 c2
    | Cursor ty1, Cursor ty2 => eq ty1 ty2
    | @InternalH n1 c1 ty1, @InternalH n2 c2 ty2 =>
      match eq_nat_dec n1 n2 with
        | right _neqPrf => false
        | left eqPrf    =>
          codeEq c1 c2 && Vector.eqb _ eq ty1 ty2
        end
     | @InternalZ n1 c1 v1 (l1, f1), @InternalZ n2 c2 v2 (l2, f2) =>
       match eq_nat_dec n1 n2 with
       | right _neqPrf => false
       | left eqPrf    =>
         codeEq c1 c2 && Vector.eqb _ eq v1 v2 && Fin.eqb l1 l2 && eq f1 f2
       end
    | _, _ => false
    end.
End Typ.

你的问题中最难、最开放的就是第一个问题。我认为对类型进行建模的最简单方法是将其分为两部分:一种原始语法树类型和一种由树索引的路径类型。例如:

Require Fin.
Require Vector.

Set Implicit Arguments.

Module Typ.
  Inductive arityCode : nat -> Type :=
    | Num   : arityCode 0
    | Hole  : arityCode 0
    | Arrow : arityCode 2
    | Sum   : arityCode 2
    .

  Inductive t : Type :=
    | Node : forall n, arityCode n -> Vector.t t n -> t.

  Inductive path : t -> Type :=
    | Here  : forall n (c : arityCode n) (v : Vector.t t n), path (Node c v)
    | There : forall n (c : arityCode n) (v : Vector.t t n) (i : Fin.t n),
                path (Vector.nth v i) -> path (Node c v).
End Typ.

Here, path tree表示树中索引的类型tree.

Coq 社区对于如何以及何时使用依赖类型经常存在分歧。在这种特殊情况下,我认为有一个类型会更容易t原始语法树和非依赖类型path成树的路径。您可以定义表示相对于树的路径的格式良好的谓词,并在事后证明您关心的函数尊重格式良好的概念。我发现在这种情况下这更灵活,因为您不必担心在函数中操作类型索引并推理它们(要理解这意味着什么,请尝试陈述原始的正确性定理Typ.eq功能)。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Coq 平等实现 的相关文章

  • Coq 中是否有一套最小完整的策略?

    我见过很多 Coq 策略 它们在功能上是相互重叠的 例如 当你在假设中得到确切的结论时 你可以使用assumption apply exact trivial 也许还有其他人 其他例子包括destruct and induction对于无感
  • 如何禁止简单策略展开算术表达式?

    The simpl策略展开诸如2 a 匹配树 这看起来一点也不简单 例如 Goal forall i Z fun x gt x i 3 i 3 simpl 导致 forall i Z match i with 0 gt 3 Z pos y
  • 使用由明确定义的归纳定义的递归函数进行计算

    当我使用Function在 Coq 中定义一个非结构递归函数 当要求进行特定计算时 生成的对象会表现得很奇怪 事实上 不是直接给出结果 而是Eval compute in 指令返回一个相当长 通常为 170 000 行 的表达式 Coq 似
  • Coq 中的 `destruct` 和 `case_eq` 策略有什么区别?

    我明白了destruct因为它将归纳定义分解为其构造函数 我最近看到case eq我不明白它有什么不同 1 subgoals n nat k nat m M t nat H match M find elt nat n m with Som
  • 如何从外部软件调用证明助手Coq

    如何从外部软件调用证明助手Coq Coq 有一些 API 吗 Coq 命令行界面是否足够丰富 可以在文件中传递参数并在文件中接收响应 我对 Java 或 C 桥感兴趣 这是合理的问题 Coq 并不是一种常见的商业软件 人们可以从中获得开发人
  • 如何切换到 Coq 的特定版本——尤其是在使用 Opam 管理 Coq 版本时?

    我目前使用的是标准方式 可能通过网站 安装的标准方式 但我想用tcoq 我相信我已经正确安装了它 因为我有一个 bin 文件 并且所有常见的 Coq 内容似乎都在那里 pinno gamepad tcoq ls bin coq tex co
  • 在 Coq 中使用我自己的 == 运算符重写策略

    我试图直接从字段的公理证明简单的字段属性 经过对 Coq 原生现场支持的一些实验 像这个 我决定最好简单地写下 10 条公理并使其自成一体 我在需要使用的时候遇到了困难rewrite与我自己的 运算符自然不起作用 我意识到我必须添加一些我的
  • 如何在 Coq 中将一条线的公理定义为两个点

    我想找一个例子axiom in Coq类似于几何中的线公理 如果给定两个点 则这两点之间存在一条线 我想看看如何在 Coq 中定义它 本质上选择这个简单的直线公理来看看如何定义一些非常原始的东西 因为我很难在自然语言之外定义它 具体来说 我
  • 匹配中的冗余子句

    当我运行以下脚本时 Definition inv a Prop Prop match a with False gt True True gt False end 我收到 错误 该子句是多余的 知道为什么会发生这种情况吗 谢谢 马库斯 这件
  • 用约翰·梅杰的等式重写

    约翰 梅杰的等式带有以下重写引理 Check JMeq ind r JMeq ind r forall A Type x A P A gt Prop P x gt forall y A JMeq y x gt P y 很容易将其概括为 Le
  • Coq - 在 if ... then ... else 中使用 Prop (True | False)

    我对 Coq 有点陌生 我正在尝试实现插入排序的通用版本 我正在实现一个以比较器作为参数的模块 该 Comparator 实现了比较运算符 如 is eq is le is neq 等 在插入排序中 为了插入 我必须比较输入列表中的两个元素
  • 将假设中的 ~exists 转换为 forall

    我陷入了假设的境地 exists k k lt n 1 f k f n 2 并希望将其转换为等效的 我希望如此 假设forall k k lt n 1 gt f k lt gt f n 2 这是一个小例子 Require Import Co
  • 在 Coq 中证明可逆列表是回文

    这是我对回文的归纳定义 Inductive pal X Type list X gt Prop pal0 pal pal1 forall x X pal x pal2 forall x X l list X pal l gt pal x l
  • 依赖类型的 Church 编码:从 Coq 到 Haskell

    在 Coq 中 我可以为长度为 n 的列表定义 Church 编码 Definition listn A Type nat gt Type fun m gt forall X nat gt Type X 0 gt forall m A gt
  • 如何一步步检查 Coq 中更复杂的策略的作用?

    我试图经历那些著名的和精彩的软件基础书籍 https softwarefoundations cis upenn edu lf current Basics html lab30但我举了一个例子simpl and reflexivity 只
  • 如何在构造微积分中提取Sigma的第二个元素?

    我尝试这样做 A gt B A gt gt t r gt x a gt B x gt r gt r gt t B t A x A gt y B x gt x x A gt y B x gt y 请注意 由于该函数返回的值取决于 sigma
  • 在 Coq 中查找 ++ 等定义和符号

    我们如何获得这些符号的定义 类型 例如 or of List 我努力了 Search Search Search SearchAbout and Check Check Check 然而它们都不起作用 SearchAbout 确实显示了一些
  • 我如何编写行为类似于“破坏...作为”的策略?

    在coq中 destruct https coq inria fr distrib current refman Reference Manual010 html hevea tactic65策略有一个接受 连接析取引入模式 的变体 该模式
  • Coq:承认断言

    有没有办法在 Coq 中承认断言 假设我有一个这样的定理 Theorem test forall m n nat m n n m Proof intros n m assert H1 m m n m S n Admitted Abort 上
  • 教 coq 检查终止

    Coq 与许多其他参数不同 它接受一个可选的显式参数 该参数可用于指示定点定义的递减结构 根据 Gallina 规范 1 3 4 Fixpoint ident params struct ident0 type0 term0 定义语法 但从

随机推荐