以下两种类型是等效的:
unit -> ([record(fn: unit -> fix)] as fix) (A)
[unit -> record(fn: fix)] as fix (B)
然而,通过展开或替换都不能从另一个获得,也没有任何更通用的类型可以从这种方式得到它们(AFAICS)。显然,第二种类型是最小的(因为它是递归的)。鉴于这种类型,是否有最小化的算法?显然,每个CAN都来自另一个,因为它们使用跟踪统一。
当记录中包含的函数返回与记录相同类型的值时,会出现这些类型。我的问题是我的编译器在目标语言中生成两个不同的不兼容表示。
一般情况下,给定任何一个循环,我们可以通过选择一个起始点,部分地围绕圆圈,选择一个固定点,然后再一次运行到固定点来推导出一种类型。
具有不同起点的两种类型是不同的,但可以通过将一种类型回溯到另一种的起点来使它们相等。这是一张图片:
A: function <<<<<<<<<<<<<<<<<+
unit |
record <---------+ |
field | |
"fn" | |
B: function >>>> | >>>>+ B^
unit |
record -----+ A^
答案 0 :(得分:1)
我认为关于检查递归类型等式问题的第一次完整讨论是Roberto Amadio和Luca Cardelli的1991 Subtyping recursive types。特别是,他们指出,仅考虑有限展开并不足以决定递归类型之间的相等性(请参阅第13页开头的注释,以及我们现在编写的示例mu a. t -> t -> a
和mu a. t -> a
: “语义均等”(无限展开的等式)比仅仅展开的等式更丰富。然后他们继续提出一种算法来检查语义相等。这个算法经过了很多改进,提出了各种更高效的版本,并且理解共同归纳是解决这个问题的正确方法(参见例如Subtyping Recursive Types, Revealed,Vladimir Gapeyev,Michael Y. Levin,Benjamin Pierce,2000)。
Amadio和Cardelli算法的工作方式是直接比较这对类型(而不是尝试规范化类型并检查相等性),并记住(以通常的共感方式)它们的等式“目前试图在一系列假设中证明”。如果在进入某些类型构造函数之后,您回到其中一个假设,您可以绑定证明结并声称类型相等。在你的例子中,将:
unit -> ([record(fn: unit -> fix)] as fix)
=?= (1)
[unit -> record(fn: fix)] as fix
unfold the second type
unit -> ([record(fn: unit -> fix)] as fix)
=?= (2)
unit -> record(fn: [unit -> record(fn: fix)] as fix)
peel the common (unit ->) part.
[record(fn: unit -> fix)] as fix
=?= (3)
record(fn: [unit -> record(fn: fix)] as fix)
unfold the first type
record(fn: unit -> [record(fn: unit -> fix)] as fix)
=?= (4)
record(fn: [unit -> record(fn: fix)] as fix)
peel the common record(fn : ) part
unit -> [record(fn: unit -> fix)] as fix
=?= (1)
[unit -> record(fn: fix)] as fix
(5) is the same as the previously-seen equation (2), so the types are
equal by assumption.
你提出了一个不同的问题:不是如何决定递归类型之间的平等,而是是否存在递归类型的“规范”表示,例如两个等价的递归类型具有相同的规范表示 - 在你的命题中,如果我理解正确的话,通过取消包装操作。 (请注意,Amadio和Cardelli的论文提到了“规范表征”,但是他们使用这个词来表达一个不同的概念,事实上,这个概念在上面的意义上并不是规范的。)
我不明白你的命题的细节,但我并不是先验地确信存在这样的规范表示。对于给定的对类型,可以检查它们的等价性,如果它们确实是等价的,我认为你可以将等价证明作为一系列转换回读(可能与你的{{1相关)操作)将它们都转换为相同的“较小的公共多重”表示。但是,对于一对类型,给出了“较小的公共倍数”(或者,如果您愿意,最常见的统一者);给定类型与其他类型进行比较时,它将具有无限多个不同的“共同多重”。当然,你的算法不是通过展开/展开类型来产生规范表示,而是通过折叠/包装它的相反操作,因此它可能仍然有用。
您真的需要这种规范表示,还是在寻找算法来计算递归类型的相等性?
答案 1 :(得分:0)
进一步想到我认为我有一个部分答案。首先是视觉图片:一条带有彩色部分的字符串,表示组合器,其尾部用绳子系在一起打结,在底部形成一个定向环。 (结当然是固定点,它附加的点是绑定点...... :)。
该路径的算法是将循环向上滚动一级以查看尾部段是否与结之前的线性部分的尾部具有相同的颜色。如果是这样,请再试一次,直到你有一个循环或匹配失败。
就类型术语而言,从根向下运行到每个叶子。如果传递一个fixpoint binder并以一个fixpoint结束,请尝试将包含fixpoint的术语与包含fixpoint binder的术语匹配。如果它们匹配,则将包含fixpoint的术语替换为fixpoint,并将fixpoint binder向上移动一级路径。重复。
对每条路径重复一次。结果不是最小的,因为它不处理一次展开的递归项,但结果可以重新折叠以移除展开。所以大致有两个递归下降。
但复杂性更糟。这是因为,例如,对于二元组合子,我们沿着左路径行进,组合器的“颜色”是整个右手子树的组合器PLUS。我们不能简单地比较右手路径,因为它们可能尚未最小化,而是我们必须使用跟踪的结构比较。哎哟。
编辑:这是我在Ocaml中的折叠例程,它重新展开递归术语。编码使用隐式的固定点绑定器,固定术语只记录最多的级别(负数),这消除了对mu变量的需要。
let fold t =
let rec aux trail depth t' =
let ax t = aux ((depth,t')::trail) (depth+1) t in
match t' with
| BTYP_record (ls) -> List.iter (fun (s,t) -> ax t) ls
| BTYP_function (a,b) -> ax a; ax b
| BTYP_fix (0,_) -> ()
| BTYP_fix (i,_) ->
let k = depth + i in
begin try
let t'' = List.assoc k trail in
if type_eq t'' t then raise (Found t'')
with Not_found -> ()
end
in
try aux [] 0 t; t
with Found t -> t
let minimise bsym_table counter t =
fold (map ~f_btype:fold t)
最小化不起作用。我们的想法是在修复点处理程序中汇总跟踪,通过比较跟踪中的条件,但是在编写时不能在折叠例程中完成。