【问题标题】:exposing the structure of inductively defined terms in coq暴露 coq 中归纳定义项的结构
【发布时间】:2012-09-05 05:04:54
【问题描述】:

在简单类型的 lambda 演算中类型推导是唯一的证明在纸上是微不足道的。我熟悉的证明是通过对类型推导的完全归纳来进行的。但是,我无法证明类型派生(通过 类型派生的类型表示)是唯一的。这里,如果变量x 在环境Γ 中具有τ 类型,则谓词dec Γ x τ 为真。类型谓词 J 像往常一样定义,只是读取简单类型 lambda 演算的类型规则:

Inductive J (Γ : env) : term → type → Set :=
  | tvar : ∀ x τ, dec Γ x τ → J γ (var x) τ
  | tabs : ∀ τ₁ τ₂ e, J (τ₁ :: γ) e τ₂ → J γ (abs τ₁ e) (arr τ₁ τ₂)
  | tapp : ∀ τ₁ τ₂ e₁ e₂, J γ e₁ (arr τ₁ τ₂) → J γ e₂ τ₁ → J γ (app e₁ e₂) τ₂.

在证明类型派生是唯一的时,我无法公开 J 类型的术语的结构。例如,我可以在以下引理中对d1d2 进行归纳,但不能对d1 进行归纳,然后破坏d2,反之亦然。 Coq 给出的错误消息(对术语进行抽象会导致输入错误的术语)有点模糊,并且 Coq wiki 没有提供任何帮助。作为参考,这是我试图证明的引理:

Lemma unique_derivation : ∀ Γ e τ (d₁ d₂ : J Γ e τ), d₁ = d₂.

我在归纳术语时没有问题,例如,在证明类型是唯一的时。

编辑:我添加了最少数量的定义来说明我遇到问题的结果。作为对 huitseeker 评论的回应,我选择了 J 类型,因为我想将派生类型作为结构化对象进行推理,以便执行提取等操作并证明唯一性等结果,这是我以前在 Coq 中没有做过的。

针对评论的第一部分,我可以在d1d2 上执行induction,但在执行induction 之后 我不能使用destruct,@ 987654339@,或induction 剩余期限。这意味着我不能同时公开d1d2 的结构来推理这两个证明树。当我尝试这样做时收到的错误是,对剩余术语进行抽象会导致术语类型错误。

Require Import Unicode.Utf8.
Require Import Utf8_core.
Require Import List.

Inductive type : Set :=
  | tau : type
  | arr : type → type → type.

Inductive term : Set :=
  | var : nat → term
  | abs : type → term → term
  | app : term → term → term.

Definition dec (Γ : list type) x τ : Prop :=
  nth_error γ x = Some τ.

Inductive J (Γ : list type) : term → type → Set :=
  | tvar : ∀ x τ, dec Γ x τ → J Γ (var x) τ
  | tabs : ∀ τ₁ τ₂ e, J (τ₁ :: Γ) e τ₂ → J Γ (abs τ₁ e) (arr τ₁ τ₂)
  | tapp : ∀ τ₁ τ₂ e₁ e₂, J Γ e₁ (arr τ₁ τ₂) → J Γ e₂ τ₁ → J Γ (app e₁ e₂) τ₂.

Lemma derivations_unique : ∀ Γ e τ (d1 d2 : J Γ e τ), d1 = d2.
Proof. admit. Qed.

我尝试过使用dependent inductionCoq.Logic 库中的几个结果,但没有成功。推导是独一无二的,这似乎是一个容易证明的命题。

【问题讨论】:

  • 请更准确地说您尝试过的内容('a term which is ill-typed' 对于您的特定问题可能有多种含义 - 是 Coq 还是您进行打字?-,您使用 destruct 作为动词,但这可能意味着 casedestruct 或其他)。最后,您确定J 的类型吗?你熟悉预测性的概念吗?
  • 我熟悉谓词的概念,Set 在 Coq 中是谓词。我有限的理解向我表明这不是问题。我编辑了原始帖子以反映陈述定理所需的最小发展,并尝试更详细地描述我遇到的问题。
  • @huitseeker “类型错误”可识别为 Coq 错误,但实际证明尝试和准确错误消息的复制粘贴确实会受到赞赏。如果您想将它们作为一流的对象进行推理,那么在Set 中包含J 是正确的,比如证明它们的唯一性。

标签: lambda-calculus coq


【解决方案1】:

你有三个问题。

一个是使感应工作的纯技术问题。您可以使用dependent destruction 策略解决主要困难(由Matthieu Sozeau on the Coq-Club mailing list 提供)。这是一种反转策略。我不会假装理解它是如何工作的。

第二个困难是在一个基本案例中,对于环境。你需要证明list nat 中的等式证明是唯一的;这适用于所有可判定的域,其工具位于 Eqdep_dec 模块中。

第三个困难与问题有关。推导的唯一性不会跟随术语或推导结构的直接归纳,因为您的术语没有携带足够的类型信息来重构推导。在应用程序app e1 e2 中,没有直接的方法可以知道参数的类型。在简单类型的 lambda 演算中,类型重构确实成立,并且很容易证明;在较大的演算中(具有多态性或子类型),它可能不成立(例如,对于 ML 风格的多态性,有一个独特的 principal 类型方案和相关的派生,但有许多使用基类型的派生) .

这是你引理的快速证明。我省略了环境查找唯一性的证明。您可以对术语结构或推导结构进行归纳——这个简单的证明很有效,因为它们是相同的。

Require Import Unicode.Utf8.
Require Import Utf8_core.
Require Import List.
Require Import Program.Equality.

Inductive type : Set :=
  | tau : type
  | arr : type → type → type.

Inductive term : Set :=
  | var : nat → term
  | abs : type → term → term
  | app : term → term → term.

Definition dec (Γ : list type) x τ : Prop :=
  nth_error Γ x = Some τ.

Inductive J (Γ : list type) : term → type → Set :=
  | tvar : ∀ x τ, dec Γ x τ → J Γ (var x) τ
  | tabs : ∀ τ₁ τ₂ e, J (τ₁ :: Γ) e τ₂ → J Γ (abs τ₁ e) (arr τ₁ τ₂)
  | tapp : ∀ τ₁ τ₂ e₁ e₂, J Γ e₁ (arr τ₁ τ₂) → J Γ e₂ τ₁ → J Γ (app e₁ e₂) τ₂.

Lemma unique_variable_type :
  forall G x t1 t2, dec G x t1 -> dec G x t2 -> t1 = t2.
Proof.
  unfold dec; intros.
  assert (value t1 = value t2). congruence.
  inversion H1. reflexivity.
Qed.

Axiom unique_variable_type_derivation :
  forall G x t (d1 d2 : dec G x t), d1 = d2.

Lemma unique_type : forall G e t1 t2 (d1 : J G e t1) (d2 : J G e t2), t1 = t2.
Proof.
  intros G e; generalize dependent G.
  induction e; intros.

  dependent destruction d1. dependent destruction d2.
  apply (unique_variable_type G n); assumption.

  dependent destruction d1. dependent destruction d2.
  firstorder congruence.

  dependent destruction d1. dependent destruction d2.
  assert (arr τ₁ τ₂ = arr τ₁0 τ₂0).
  firstorder congruence.
  congruence.
Qed.

Lemma unique_derivation : forall G e t (d1 d2 : J G e t), d1 = d2.
Proof.
  intros G e; generalize dependent G.
  induction e; intros.

  dependent destruction d1. dependent destruction d2.
  f_equal. solve [apply (unique_variable_type_derivation G n)].

  dependent destruction d1. dependent destruction d2.
  f_equal. solve [apply IHe].

  dependent destruction d1. dependent destruction d2.
  assert (τ₁ = τ₁0). 2: subst τ₁.
  solve [eapply unique_type; eauto].
  f_equal; solve [firstorder].
Qed.

【讨论】:

  • 谢谢。看起来我有一些关于依赖平等的阅读。关于您的第三条评论:我知道键入是具有参数多态性的 lambda 演算的主要类型方案的唯一模模,等等。当您说证明仅适用于简单类型的 lambda 演算中类型和期限结构之间存在同构时,您的意思是说,对于具有更具表现力的类型系统的演算,证明的结构必然会有所不同,甚至如果您使用的是用户定义的等价而不是 Leibniz 身份?
  • @danportin 此处的证明有效,因为术语和派生(而非类型)结构之间存在同构。一般来说,我希望经历一个中间类型重构引理,它将术语转换为带有附加类型注释的术语(包括应用程序上的函数和参数类型)。这里独特的打字引理足以进步,但我预计这对于“有趣”的演算会很快变得更加复杂。我对推导唯一性没有太多经验,所以对此持保留态度。
  • 对不起,我的意思是术语结构和类型推导之间的同构。谢谢你提供的详情。我不确定你的意思是不是因为简单类型的 lambda 演算的特殊性,Coq 证明的结构有效,并且不适用于其他 lambda 演算。谢谢你的澄清。