为什么Coq的相互归纳类型必须具有相同的参数?

5

根据Arthur的建议,我将我的Fixpoint关系更改为相互的Inductive关系,该关系"建立"游戏之间的不同比较,而不是"向下钻取"。

但现在我收到了一个全新的错误信息:

Error: Parameters should be syntactically the same for each inductive type.

我认为错误信息的意思是,所有这些相互感应的定义都需要完全相同的参数。
我知道有一些简单的方法可以绕过这个问题(未使用的虚拟变量、长的函数类型,其中所有内容都在“forall”内部),但我不明白为什么我必须这样做。
有人能解释一下相互感应类型限制背后的逻辑吗?有更优雅的写法吗?这种限制是否意味着彼此之间的归纳调用都必须使用相同的参数(在这种情况下,我不知道任何绕过这个问题的方法,除了可怕的大量代码复制)?
(所有类型和术语的定义,例如compare_quest、game、g1side等,在first question中都没有改变。)
Inductive gameCompare (c : compare_quest) : game -> game -> Prop :=
 | igc : forall g1 g2 : game,
    innerGCompare (nextCompare c) (compareCombiner c) (g1side c) (g2side c) g1 g2 ->
    gameCompare c g1 g2
with innerGCompare (next_c : compare_quest) (cbn : combiner) (g1s g2s : side)
    : game -> game -> Prop :=
 | compBoth : forall g1 g2 : game,
    cbn (listGameCompare next_c cbn (g1s g1) g2)
        (gameListCompare next_c cbn g1 (g2s g2)) ->
    innerGCompare next_c cbn g1s g2s g1 g2
with listGameCompare (c : compare_quest) (cbn : combiner) : gamelist -> game -> Prop :=
 | emptylgCompare : cbn_init cbn -> forall g2 : game, listGameCompare c cbn emptylist g2
 | otlgCompare : forall (g1_cdr : gamelist) (g1_car g2 : game),
    (cbn (listGameCompare c cbn g1_cdr g2) (gameCompare c g1_car g2)) ->
    listGameCompare c cbn (listCons g1_car g1_cdr) g2
with gameListCompare (c : compare_quest) (cbn : combiner) : game -> gamelist -> Prop :=
 | emptyglCompare : cbn_init cbn -> forall g1 : game, gameListCompare c cbn g1 emptylist
 | otglCompare : forall (g1 g2_car : game) (g2_cdr : gamelist),
    (cbn (gameListCompare c cbn g1 g2_cdr) (gameCompare c g1 g2_car)) ->
    gameListCompare c cbn g1 (listCons g2_car g2_cdr).

在CGT中,通常有两个玩家(称为LeftRight)轮流进行游戏,最后一个移动的玩家获胜。每个游戏(意味着每个游戏中的每个位置)可以被看作是Left选项的集合和Right选项的集合,写成{G_L | G_R}。比较两个游戏时,它们可以以四种不同的方式进行比较:<>=||
如果B对于Left来说比A严格更好,无论谁先走,那么游戏A < B。如果A对于Left来说比B更好,则A > B。如果两个游戏等价(在这样的意义上,即游戏A + -B的总和是零游戏,因此先手的玩家输),则A = B。如果哪个游戏对于Left更好取决于谁先走,则A || B

比较两个游戏的一种方法如下:

  • 如果A的所有Left子节点都小于或等于B,并且AB的所有右子节点的父节点,则A <= B

  • 如果A有一个右子节点小于或等于B,或者A小于或等于B的任何左子节点,则A <| B

  • 同样地,对于>=>|也是如此。

因此,通过观察这些关系中哪一对适用于游戏 AB,可以确定是 A < B (A<=BA<|B)、A=B (A<=BA>=B)、A > B (A>=BA>|B),还是 A || B (A<|BA>|B)。

这里是 CGT维基百科文章

1个回答

2
这个限制很有趣,我以前从未遇到过。我没有看到任何原因可以解释为什么应该拒绝这段代码。我猜测,在设计Coq的基础时,这个限制使一些证明更容易。由于没有多少人被困扰,所以它就一直保留了下来。当然,我也可能完全错了;我知道参数和参数(即指向箭头右侧的内容)在某些方面的处理略有不同。例如,在定义归纳类型时强制执行的宇宙约束对参数比对参数的要求较少。
也许应该将此转发到Coq俱乐部邮件列表? :)
你不必将所有内容都放在箭头右侧才能使其正常工作。你可以做的一件事是将除“compare_quest”参数之外的所有内容都放在右侧。当你在构造函数中使用正在定义的相同类型的归纳类型时,你给出的参数不必与标题中给出的参数相同,所以那样做没问题:
Inductive gameCompare (c : compare_quest) : game -> game -> Prop :=
 | igc : forall g1 g2 : game,
    innerGCompare (nextCompare c) (compareCombiner c) (g1side c) (g2side c) g1 g2 ->
    gameCompare c g1 g2

with innerGCompare (c : compare_quest) : combiner -> side -> side ->
    game -> game -> Prop :=
 | compBoth : forall cbn g1s g2s (g1 g2 : game),
    cbn (listGameCompare c cbn (g1s g1) g2)
        (gameListCompare c cbn g1 (g2s g2)) ->
    innerGCompare c cbn g1s g2s g1 g2

with listGameCompare (c : compare_quest) : combiner -> gamelist -> game -> Prop :=
 | emptylgCompare : forall cbn, cbn_init cbn -> forall g2 : game, listGameCompare c cbn emptylist g2
 | otlgCompare : forall cbn (g1_cdr : gamelist) (g1_car g2 : game),
    (cbn (listGameCompare c cbn g1_cdr g2) (gameCompare c g1_car g2)) ->
    listGameCompare c cbn (listCons g1_car g1_cdr) g2

with gameListCompare (c : compare_quest) : combiner -> game -> gamelist -> Prop :=
 | emptyglCompare : forall cbn, cbn_init cbn -> forall g1 : game, gameListCompare c cbn g1 emptylist
 | otglCompare : forall cbn (g1 g2_car : game) (g2_cdr : gamelist),
    (cbn (gameListCompare c cbn g1 g2_cdr) (gameCompare c g1 g2_car)) ->
    gameListCompare c cbn g1 (listCons g2_car g2_cdr).

很遗憾,尝试评估此项时出现了新的错误 :(
Error: Non strictly positive occurrence of "listGameCompare" in
 "forall (cbn : Prop -> Prop -> Prop) (g1s g2s : game -> gamelist)
    (g1 g2 : game),
  cbn (listGameCompare c cbn (g1s g1) g2) (gameListCompare c cbn g1 (g2s g2)) ->
  innerGCompare c cbn g1s g2s g1 g2".

这个错误在Coq中更为常见。它在抱怨你正在将你定义的类型作为参数传递给cbn,因为这可能导致该类型出现在箭头的左侧(负发生),这已知会导致逻辑不一致。
我认为你可以通过将compareCombiner内联到最后三种类型的构造函数中来消除这个问题,这可能需要对代码进行一些重构。同样,我相信肯定有更好的定义方式,但是我不太理解你想要做什么,所以我的帮助有些有限。
更新:我开始了一系列关于在Coq中形式化一些CGT的文章。您可以在这里找到第一篇。

谢谢,知道了。我看到问题了,我知道如何摆脱它(虽然不是没有一些代码重复)。我已经添加了比较两个CGT游戏意味着什么的描述,以防您有其他想法。再次感谢。 - dspyz
2
哇,这个CGT的东西真的很酷!感谢您更详细地解释了一下。我已经发布了一个gist,在Coq中开发了一些内容,也许这会对您有所帮助! - Arthur Azevedo De Amorim

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接