Coq 中的无限递归类型(用于 Bananas 和 Lenses)
Infinite recursive types in Coq (for Bananas and Lenses)
我希望看到 Bananas、Lenses 等的 Coq 版本。它们建立在 posts 的优秀系列博客 sumtypeofway Introduction to Recursion schemes
中
然而,博客 post 在 Haskell 中,它允许无限非终止递归,因此对 Y
组合器非常满意。哪个 Coq 不是。
特别是,定义取决于类型
newtype Term f = In { out :: f (Term f) }
构建无限类型f (f (f (f ...)))
。 Term f
允许使用 Term 类型族对变形、同态、变形等进行非常漂亮和简洁的定义。
尝试将其移植到 Coq 中
Inductive Term f : Type := {out:f (Term f)}.
给了我预期的
Error: Non strictly positive occurrence of "Term" in "f (Term f) -> Term f".
问:在 Coq 中形式化上述 Haskell 术语类型的好方法是什么?
上面的 f
是 Type->Type
类型,但也许它太笼统了,可能有一些方法可以限制我们使用归纳类型,这样 f
的每个应用程序都是减少?
也许有人已经在 Coq 中实现了 Banans, Lenses, Envelopes 的递归方案?
我认为流行的解决方案是将函子编码为 "containers", the intro of this paper is a good starting point: https://arxiv.org/pdf/1805.08059.pdf 这个想法很古老(论文的意思是给出一个独立的解释),你可以从那篇论文中追寻参考文献,但是什么我在粗略搜索中发现,如果您不熟悉类型论或范畴论,可能很难理解。
简而言之,我们使用以下类型代替 Type -> Type
:
Set Implicit Arguments.
Set Contextual Implicit.
Record container : Type := {
shape : Type;
pos : shape -> Type;
}.
粗略地,如果你想象一个"base functor" F
的递归类型 Fix F
,shape
描述了 F
的构造函数,并且对于每个构造函数,pos
枚举了其中的"holes"。所以 List
的基函子
data ListF a x
= NilF -- no holes
| ConsF a x -- one hole x
由这个容器给出:
Inductive list_shape a :=
| NilF : list_shape a
| ConsF : a -> list_shape a.
Definition list_pos a (s : list_shape a) : Type :=
match s with
| NilF => False (* no holes *)
| ConsF _ => True (* one hole x *)
end.
Definition list_container a : container := {|
shape := list_shape a;
pos := fun s => list_pos s;
|}.
关键是这个容器描述了一个严格正的函子:
Inductive ext (c : container) (a : Type) : Type := {
this_shape : shape c;
this_rec : pos c this_shape -> a;
}.
Definition listF a : Type -> Type := ext (list_container a).
所以固定点构造可以采用容器而不是 Fix f = f (Fix f)
:
Inductive Fix (c : container) : Type := MkFix : ext c (Fix c) -> Fix c.
并非所有仿函数都可以编码为容器(延续仿函数就是一个典型的例子),但您不会经常看到它们与 Fix
.
一起使用
完整要点:https://gist.github.com/Lysxia/21dd5fc7b79ced410b129f31ddf25c12
我希望看到 Bananas、Lenses 等的 Coq 版本。它们建立在 posts 的优秀系列博客 sumtypeofway Introduction to Recursion schemes
中然而,博客 post 在 Haskell 中,它允许无限非终止递归,因此对 Y
组合器非常满意。哪个 Coq 不是。
特别是,定义取决于类型
newtype Term f = In { out :: f (Term f) }
构建无限类型f (f (f (f ...)))
。 Term f
允许使用 Term 类型族对变形、同态、变形等进行非常漂亮和简洁的定义。
尝试将其移植到 Coq 中
Inductive Term f : Type := {out:f (Term f)}.
给了我预期的
Error: Non strictly positive occurrence of "Term" in "f (Term f) -> Term f".
问:在 Coq 中形式化上述 Haskell 术语类型的好方法是什么?
上面的 f
是 Type->Type
类型,但也许它太笼统了,可能有一些方法可以限制我们使用归纳类型,这样 f
的每个应用程序都是减少?
也许有人已经在 Coq 中实现了 Banans, Lenses, Envelopes 的递归方案?
我认为流行的解决方案是将函子编码为 "containers", the intro of this paper is a good starting point: https://arxiv.org/pdf/1805.08059.pdf 这个想法很古老(论文的意思是给出一个独立的解释),你可以从那篇论文中追寻参考文献,但是什么我在粗略搜索中发现,如果您不熟悉类型论或范畴论,可能很难理解。
简而言之,我们使用以下类型代替 Type -> Type
:
Set Implicit Arguments.
Set Contextual Implicit.
Record container : Type := {
shape : Type;
pos : shape -> Type;
}.
粗略地,如果你想象一个"base functor" F
的递归类型 Fix F
,shape
描述了 F
的构造函数,并且对于每个构造函数,pos
枚举了其中的"holes"。所以 List
data ListF a x
= NilF -- no holes
| ConsF a x -- one hole x
由这个容器给出:
Inductive list_shape a :=
| NilF : list_shape a
| ConsF : a -> list_shape a.
Definition list_pos a (s : list_shape a) : Type :=
match s with
| NilF => False (* no holes *)
| ConsF _ => True (* one hole x *)
end.
Definition list_container a : container := {|
shape := list_shape a;
pos := fun s => list_pos s;
|}.
关键是这个容器描述了一个严格正的函子:
Inductive ext (c : container) (a : Type) : Type := {
this_shape : shape c;
this_rec : pos c this_shape -> a;
}.
Definition listF a : Type -> Type := ext (list_container a).
所以固定点构造可以采用容器而不是 Fix f = f (Fix f)
:
Inductive Fix (c : container) : Type := MkFix : ext c (Fix c) -> Fix c.
并非所有仿函数都可以编码为容器(延续仿函数就是一个典型的例子),但您不会经常看到它们与 Fix
.
完整要点:https://gist.github.com/Lysxia/21dd5fc7b79ced410b129f31ddf25c12