使用定理信息的模式匹配

Сер*_*жко 2 pattern-matching coq dependent-type convoy-pattern

我有以下问题,请查看代码。

  (* Suppose we have type A *)
  Variable A: Type.

  (* Also we have a function that returns the type (option A) *)
  Definition f_opt x: option A := ...

  (* Then, I can prove that this function always returns something: *)
  Theorem always_some: forall x, exists y, f_opt x = Some y.
  Admitted.

  (* Or, equivalently: *)
  Theorem always_not_none: forall x, f_opt x <> None.
  Admitted.
Run Code Online (Sandbox Code Playgroud)

现在我想得到一个f_opt总是返回 type 值的版本A。像这样的东西:

  Definition f x: A :=
    match f_opt x with
      | Some y => y
    end.
Run Code Online (Sandbox Code Playgroud)

但我收到以下错误:

非详尽模式匹配:没有找到用于模式的子句None

我知道我需要对类型做一些工作,但我不明白我到底应该做什么。

Art*_*rim 6

在 Coq 的基础理论中,每个模式匹配都必须是详尽无遗的——也就是说,它必须明确考虑所讨论的归纳类型的所有构造函数。这就是您收到所看到的错误消息的原因。

我们如何绕过这个限制?有几种解决方案。首先,让我们看看如何让 Coq 相信None分支永远不会发生。为此,我们将使用您的always_not_none定理:

Definition f x : A :=
  match f_opt x as res return res <> None -> A with
  | Some y => fun _ => y
  | None => fun H => match H eq_refl with end
  end (always_not_none x).
Run Code Online (Sandbox Code Playgroud)

这段代码乍一看可能很奇怪,但它几乎可以执行您想要的模式匹配。为了向 Coq 解释这种None情况永远不会出现,它结合always_not_nonef_opt x = None在该分支上推导出矛盾的事实。这是H eq_refl那个分支上的术语。那么,match这个矛盾就足以让 Coq 相信这个分支是虚假的。更正式一点,因为False这个矛盾的命题是在没有任何构造函数的情况下定义的,当我们匹配一个 type 的术语时False,没有分支需要处理,整个表达式可以返回我们想要的任何类型——在这种情况下, A.

这段代码的奇怪之处在于匹配上的类型注释,它返回一个函数而不是A直接返回类型的东西。这是因为依赖模式匹配在 Coq 中是如何工作的:每当我们想要利用我们从匹配的特定分支中获得的信息时(这里,它f_opt x等于None在那个分支中),我们必须明确地使match 返回一个函数——Adam Chlipala 称之为convoy 模式。这样做是为了让 Coq 知道你打算在哪里使用这些额外信息并检查它是否正确完成。在这里,我们使用 that f_opt xisNone来提供always_not_none x推导矛盾所需的假设。

虽然这可以解决您的问题,但我通常会建议您不要这样做。例如,如果您知道您的A类型由某个元素占据a : A,那么您可以简单地在该分支上f返回a。这样做的好处是避免在函数中提及证明,这在简化和重写术语时经常会遇到障碍。


Dan*_*ler 5

使用 Coq 的Program模块,你可以编写一个详尽的模式匹配,但注释一些分支应该是不可能到达的,然后提供证明是这样的:

Require Import Program.
Program Definition f x : A :=
match f_opt x with
| Some a => a
| None => !
end.
Next Obligation.
destruct (always_some x). congruence.
Qed.
Run Code Online (Sandbox Code Playgroud)

(该Program模块在幕后做了很多工作,在一个完整的显式定义中,您必须使用“convoy 模式”来编写。但是请注意,当依赖时,有时Program往往会生成大量依赖JMeq和公理JMeq_eq涉及类型,即使可能没有必要。)