如何在Coq中使用有关此函数类型的已知信息

时间:2018-02-24 05:09:07

标签: variadic-functions coq idris dependent-type

假设我有以下类型typ代表bool或nat:

Inductive typ : Type := TB | TN.

我还有一个从typ列表和结果类型中提取实际函数类型的函数:

Fixpoint get_types (s: seq typ) (result_type: Type) : Type :=
match s with
| nil => result_type
| x :: xs => match x with
  | TN => nat -> get_types xs result_type
  | TB => bool -> get_types xs result_type
  end
end.
Example get_types_works : get_types (TB :: TN :: nil) nat = bool -> nat -> nat.
Proof. by []. Qed.

现在,我有另一个函数,它将s的{​​{1}}列表typget_types s类型的函数作为输入:

Fixpoint app (s: seq typ) (constructor: get_types s nat) : nat := 
match s with
| nil => 2 (* Not properly handling empty list case for now *)
| TB :: nil => constructor true
| TN :: nil => constructor 2
| TB :: xs => app xs (constructor true) 
| TN :: xs => app xs (constructor 2)
end. 

在行| TB :: nil => constructor true处使用以下内容定义上述函数失败:

Illegal application (Non-functional construction): 
The expression "constructor" of type "get_types s nat" cannot be applied to the term
 "true" : "bool"

鉴于我们知道get_types s nat的类型应为bool -> nat,因为s的值为TB :: nil,我想知道是否有&#39} ;我们可以让Coq意识到这一点,以便可以定义上述功能吗?

如果没有,这是Coq的限制还是同样适用于其他依赖类型的语言?

编辑:对于上下文,这不是我试图解决的原始问题;它是一个精简版本,用于显示我在类型系统中遇到的问题。在实际版本中,2类数据结构不是硬编码truetyp,而是携带从内存切片和验证函数解析的数据索引。 app的目标是获取包含此类类型的记录的此类typ列表,切片和构造函数的函数,然后根据类型的索引构造该记录的实例解析,或者如果任何验证失败,则返回None

2 个答案:

答案 0 :(得分:3)

原则上你想要什么都没有错。但是,至少在Coq中,有一些关于如何对模式匹配进行类型检查的简单规则,以便可以在类型中使用有关使用哪个构造函数的信息。表面语言(在本例中为Gallina)通过帮助编译(或 desugar )模式匹配来隐藏这种简单性,因此作为用户,您可以编写比底层系统必须处理的更复杂的模式。我对Idris并不熟悉,但根据依赖模式匹配的复杂程度,我怀疑你遇到了类似的限制,你必须将代码放入一个系统可以键入的表格中。

在这里,您遇到了此模式匹配编译的两个限制。第一个是构造函数的类型不是基于s上的匹配而专门化的。通过计算编译器正确的get_types s nat -> nat函数可以很容易地解决这个问题。

Require Import List.
Import ListNotations.

Inductive typ : Type := TB | TN.

Fixpoint get_types (s: list typ) (result_type: Type) : Type :=
match s with
| nil => result_type
| x :: xs => match x with
  | TN => nat -> get_types xs result_type
  | TB => bool -> get_types xs result_type
  end
end.

Fail Fixpoint app (s: list typ) : get_types s nat -> nat :=
  match s with
  | nil => fun constructor => 2
  | TB :: nil => fun constructor => constructor true
  | TN :: nil => fun constructor => constructor 2
  | TB :: xs => fun constructor => app xs (constructor true)
  | TN :: xs => fun constructor => app xs (constructor 2)
  end.
(* fails due to limitation of termination checker with nested matches *)

...但是你遇到了终止检查器的第二个问题。请注意,您的匹配很复杂:它会分析s的结构及其头部和尾部(如果它是使用cons构建的)。最终,所有模式匹配都编译为单个归纳类型的嵌套模式匹配。如果你看一下这个展开,你就会将s强制转换为t::xs,然后再将xs破坏为t0::xs',最后再递归xs。不幸的是,Coq终止检查器只将其视为t0::xs',并且不会将其视为s的子项(它真的需要xs)。

我很难以类型检查的方式手动编写您的函数,但这是使用功能正确的策略编写的版本。请注意,它生成的定义比任何普通的模式匹配要复杂得多,因为它必须维护由destruct_with_eqn生成的证明。但是,该证明对于同时使用xs使终止检查程序满意并显示t0::xs'类型检查构造函数的应用程序至关重要。它可能很复杂但你仍然可以正常运行,如最后一行所示。

Fixpoint app (s: list typ) (constructor: get_types s nat) {struct s} : nat.
  destruct s as [|t xs]; simpl in *.
  exact 2.
  destruct_with_eqn xs; simpl in *.
  destruct t; [ exact (constructor true) | exact (constructor 2) ].
  destruct t; simpl in *.
  - apply (app xs).
    subst.
    exact (constructor true).
  - apply (app xs).
    subst.
    exact (constructor 2).
Defined.

Eval compute in app [TB; TN] (fun x y => if x then y+2 else y).
(* = 4
   : nat
*)

答案 1 :(得分:1)

另外两种定义app的方式。

第一个使用策略,依赖induction代替Fixpoint

Definition app (s: seq typ) (constructor: get_types s nat) : nat.
Proof.
  induction s as [|t xs].
  - exact 2.
  - destruct xs.
    + destruct t.
      * exact (constructor true).
      * exact (constructor 2).
    + destruct t.
      * exact (IHxs (constructor true)).
      * exact (IHxs (constructor 2)).
Defined.

第二个使用Gallina和复杂的模式匹配。

Fixpoint app (s: seq typ) : get_types s nat -> nat :=
  match s return get_types s nat -> nat with
  | nil => fun _ => 2
  | x :: xs =>
    match xs as xs0 return xs = xs0 -> get_types (x::xs0) nat -> nat with
    | nil => fun _ => match x return get_types (x::nil) nat -> nat with
            | TB => fun c => c true
            | TN => fun c => c 2
            end
    | _ => fun e => match e in _ = xs1 return get_types (x::xs1) nat -> nat with
           | eq_refl =>
             match x return get_types (x::xs) nat -> nat with
             | TB => fun c => app xs (c true)
             | TN => fun c => app xs (c 2)
             end
           end
    end eq_refl
  end.

当破坏xs时,我们会记住原始xs与其被破坏之间的相等。我们不需要在nil分支中使用此等式并将其丢弃为{{ 1}}。在另一个分支中,我们在相等证明(fun _)上进行模式匹配,这对应于使用此相等性的重写。在match e分支内部,我们可以使用原始eq_refl,从而进行终止检查器允许的递归调用。在模式匹配之外,我们返回xs上模式匹配所期望的正确类型。最后要做的是提供xsxs相等的证明,但它很简单xs0