我正在努力在Coq中对Free Selective Applicative Functors进行形式化,但在归纳中对具有非均匀类型参数的归纳数据类型进行了挣扎。
让我简要介绍一下我正在处理的数据类型。 在Haskell中,我们将自由选择函子编码为GADT:
data Select f a where
Pure :: a -> Select f a
Select :: Select f (Either a b) -> f (a -> b) -> Select f b
关键是第二个数据构造函数中的存在类型变量b
。
我们可以将此定义转换为Coq:
Inductive Select (F : Type -> Type) (A : Set) : Set :=
Pure : A -> Select F A
| MkSelect : forall (B : Set), Select F (B + A) -> F (B -> A) -> Select F A.
作为旁注,我使用-impredicative-set
选项对其进行编码。
Coq对此数据类型生成以下归纳原理:
Select_ind :
forall (F : Type -> Type) (P : forall A : Set, Select F A -> Prop),
(forall (A : Set) (a : A), P A (Pure a)) ->
(forall (A B : Set) (s : Select F (B + A)), P (B + A)%type s ->
forall f0 : F (B -> A), P A (MkSelect s f0)) ->
forall (A : Set) (s : Select F A), P A s
在这里,有趣的是谓词P : forall A : Set, Select F A -> Prop
,它不仅在表达式中,而且在expressions类型参数中都参数化。据我了解,归纳原理之所以具有这种特殊形式,是因为MkSelect
类型的Select F (B + A)
构造函数的第一个参数。
现在,我想证明已定义数据类型的类似第三条适用法律的陈述:
Theorem Select_Applicative_law3
`{FunctorLaws F} :
forall (A B : Set) (u : Select F (A -> B)) (y : A),
u <*> pure y = pure (fun f => f y) <*> u.
其中涉及类型Select F (A -> B)
的值,即包含函数的表达式。然而,
在此类变量上调用induction
会产生类型错误的字词。考虑一个过分简化的等式示例,该示例可以由reflexivity
证明,但不能使用induction
证明:
Lemma Select_induction_fail `{Functor F} :
forall (A B : Set) (a : A) (x : Select F (A -> B)),
Select_map (fun f => f a) x = Select_map (fun f => f a) x.
Proof.
induction x.
Coq报错:
Error: Abstracting over the terms "P" and "x" leads to a term
fun (P0 : Set) (x0 : Select F P0) =>
Select_map (fun f : P0 => f a) x0 = Select_map (fun f : P0 => f a) x0
which is ill-typed.
Reason is: Illegal application (Non-functional construction):
The expression "f" of type "P0" cannot be applied to the term
"a" : "A"
在这里,Coq无法构造在类型变量上抽象的谓词,因为语句中的反向函数应用程序类型错误。
我的问题是,如何在数据类型上使用归纳法?我看不到如何以这种方式修改归纳原理的方法,这样谓词就不会抽象该类型。我尝试使用dependent induction
,但是它一直在产生归纳假设,受类似于(A -> B -> C) = (X + (A -> B -> C))
的等式约束,我认为无法实例化。
请在GitHub上查看完整的示例:https://github.com/tuura/selective-theory-coq/blob/impredicative-set/src/Control/Selective/RigidImpredSetMinimal.v
答案 0 :(得分:1)
归纳证明是由递归定义驱动的。因此,要知道要应用归纳法,请寻找Fixpoint
。
您的Fixpoint
最有可能使用由单一类型变量Select F A
索引的字词,这正是您要使用归纳法的地方,而不是目标的最高层次。
由功能类型Fixpoint
索引的术语上的A -> B
是没有用的,因为没有任何Select
术语的子项被函数类型索引。出于同样的原因,induction
在这种情况下是没有用的。
在这里,我认为强类型练习实际上会迫使您在尝试用Coq进行任何操作之前先将所有内容写在纸上(在我看来这是一件好事)。尝试在纸上做证明,甚至不用担心类型;明确写下您想通过归纳证明的谓词。这是了解我意思的模板:
通过对
u
的归纳,我们将显示u <*> pure x = pure (fun f => f x) <*> u (* Dummy induction predicate for the sake of example. *) (* Find the right one. *) (* It may use quantifiers... *)
基本情况(设置
u = Pure f
)。证明:Pure f <*> pure x = pure (fun f => f x) <*> Pure f
归纳步骤(设置
u = MkSelect v h
)。证明:MkSelect v h <*> pure x = pure (fun f => f x) <*> MkSelect v h
假设子项
v
(集合u = v
)的归纳假设:v <*> pure x = pure (fun f => f x) <*> v
尤其要注意,最后一个方程式的类型错误,但您仍然可以与它一起进行方程式推理。无论如何,可能会发现在简化目标之后无法应用该假设。
如果您确实需要使用Coq进行一些探索,则有一个技巧,包括删除有问题的类型参数(以及所有依赖于该参数的术语)。根据您对Coq的熟悉程度,该提示可能比什么都更令人困惑。因此请小心。
这些术语仍将具有相同的递归结构。请记住,证明也应遵循相同的结构,因为这样做的目的是在事后在顶部添加更多类型,因此,如果可以的话,应避免依赖于缺少类型的快捷方式。
(* Replace all A and B by unit. *)
Inductive Select_ (F : unit -> Type) : Set :=
| Pure_ : unit -> Select_ F
| MkSelect_ : Select_ F -> F tt -> Select_ F
.
Arguments Pure_ {F}.
Arguments MkSelect_ {F}.
(* Example translating Select_map. The Functor f constraint gets replaced with a dummy function argument. *)
(* forall A B, (A -> B) -> (F A -> F B) *)
Fixpoint Select_map_ {F : unit -> Type} (fmap : forall t, unit -> (F t -> F t)) (f : unit -> unit) (v : Select_ F) : Select_ F :=
match v with
| Pure_ a => Pure_ (f a)
| MkSelect_ w h => MkSelect_ (Select_map_ fmap f w) (fmap _ tt h)
end.
以此为例,您可以尝试证明该修整后的仿函数定律,例如:
Select_map_ fmap f (Select_map_ fmap g v) = Select_map_ fmap (fun x => f (g x)) v
(* Original theorem:
Select_map f (Select_map g v) = Select_map (fun x => f (g x)) v
*)
关键是删除参数避免了相关的键入问题,因此您可以尝试天真地使用归纳法来了解事情(不)如何解决。