我能把Coq的证明提取成Haskell函数吗?

28
  • 自从我学了一点Coq,我就想学习编写所谓的除法算法的Coq证明: forall n m : nat, exists q : nat, exists r : nat, n = q * m + r
  • 最近我用Software Foundations学到的知识完成了这个任务。
  • Coq是一个开发构造性证明的系统,我的证明实际上是一种从值mn中构造适当的值qr的方法。
  • Coq有一个有趣的机制可以将Coq算法语言(Gallina)中的算法提取到包括Haskell在内的通用函数式编程语言中。
  • 另外,我已经成功地将divmod操作作为Gallina Fixpoint编写并提取了出来。我要小心地指出,这个任务不是我在这里考虑的。
  • Adam Chlipala在Certified Programming with Dependent Types中写道,“许多柯里-霍华德对应的支持者支持从证明中提取程序的想法。事实上,几乎没有Coq和相关工具的用户会这样做。”

是否可能将我证明中的隐含算法提取到Haskell?如果可能,该如何操作?


下一步:使用将“nat”转换为“int”的提取,以便我可以正确地将4195835除以3145727,因为一些著名的芯片在浮点而不是整数或有理数上运行失败了。 - minopret
缺少条件 0 <= r < m ... - Alexey
@Alexey 好观点。我提出三点观察:1 实际上,条件 0 <= r 存在于 r 是类型 nat 的成员。2 但是,是的,糟糕的是,缺少条件 r < m。因此,更有效的 Haskell 函数是 divalg n m = Ex_t_intro O (Ex_t_intro n __)。3 然而,我从另一个证明中提取了 Coq 提取 Haskell 代码,而不是将 q 设置为零并将 r 设置为 n 的证明。因此,给定两个 Nat 参数,我的答案中的 Haskell(我声称)满足 r < m 的条件,其中 r 是结果中的第二个 Nat,而 m 是参数中的第二个 Nat - minopret
2个回答

21
由于 Pierce教授在2012年夏季课程的视频4.1,正如Dan Feltey所建议的那样,我们看到提取的定理必须提供Type的成员而不是通常类型的命题,即Prop
对于特定的定理,受影响的构造是归纳命题ex及其符号exists。与Pierce教授所做的类似,我们可以声明自己的替代定义ex_texists_t,将Prop的出现替换为Type的出现。
以下是exexists的常规重新定义,类似于它们在Coq标准库中的定义。
Inductive ex (X:Type) (P : X->Prop) : Prop :=
ex_intro : forall (witness:X), P witness -> ex X P.

Notation "'exists' x : X , p" := (ex _ (fun x:X => p))
(at level 200, x ident, right associativity) : type_scope.

这里是备选定义。

Inductive ex_t (X:Type) (P : X->Type) : Type :=
ex_t_intro : forall (witness:X), P witness -> ex_t X P.

Notation "'exists_t' x : X , p" := (ex_t _ (fun x:X => p))
(at level 200, x ident, right associativity) : type_scope.

现在,有点不幸的是,需要使用这些新定义重复定理的陈述和证明

世界上到底发生了什么??

为什么需要使用量词符的替代定义来重复定理的陈述和证明?

我希望使用现有的Prop中的定理来证明Type中的定理。但当Coq对环境中使用exists的Prop和目标为使用exists_t的Type时拒绝证明策略inversion时,这个策略失败了。Coq报告“错误:反演需要对排序Set进行情况分析,而对于归纳定义ex,这是不允许的。”这种行为在Coq 8.3中发生过。我不确定它是否仍然存在于Coq 8.4中。

我认为需要重复证明实际上是深奥的,尽管我个人怀疑自己是否能够感知其深奥性。它涉及事实,即Prop是“非谓词化的”,而Type不是非谓词化的,而是含蓄地“分层”的。如果我理解正确,谓词性是面对罗素悖论(S是那些不是它们本身成员的集合,既不能是S的成员,也不能是S的非成员)的脆弱性。Type通过含蓄地创建包含较低类型的一系列更高类型来避免Russell's paradox。由于Coq浸润在Curry-Howard对应的公式即类型解释中,如果我理解正确,我们甚至可以将Coq中的类型分层理解为避免Gödel不完全性的一种方式,即某些公式会表达对自身或其他公式等约束条件,从而无法知道它们的真假。

回到地球上,这里是使用"exists_t"重复陈述定理的内容。

Theorem divalg_t : forall n m : nat, exists_t q : nat,
  exists_t r : nat, n = plus (mult q m) r.

由于我省略了 divalg 的证明,因此我也将省略 divalg_t 的证明。我只会提到我们很幸运地发现证明策略包括“存在”和“反演”与我们的新定义“ex_t”和“exists_t”一样适用。

最后,提取本身很容易实现。

Extraction Language Haskell.

Extraction "divalg.hs" divalg_t.

生成的Haskell文件包含许多定义,其中核心是相当不错的代码(请参考下文)。虽然我对Haskell编程语言几乎一无所知,但只受到了轻微的阻碍。请注意,Ex_t_intro创建了一个类型为Ex_t的结果;OS是Peano算术中的零和后继函数;beq_nat测试Peano数是否相等;nat_rec是一个高阶函数,其参数中包含该函数的递归。在此未显示nat_rec的定义。无论如何,它是由Coq根据在Coq中定义的归纳类型"nat"生成的。

divalg :: Nat -> Nat -> Ex_t Nat (Ex_t Nat ())
divalg n m =
  case m of {
   O -> Ex_t_intro O (Ex_t_intro n __);
   S m' ->
    nat_rec (Ex_t_intro O (Ex_t_intro O __)) (\n' iHn' ->
      case iHn' of {
       Ex_t_intro q' hq' ->
        case hq' of {
         Ex_t_intro r' _ ->
          let {k = beq_nat r' m'} in
          case k of {
           True -> Ex_t_intro (S q') (Ex_t_intro O __);
           False -> Ex_t_intro q' (Ex_t_intro (S r') __)}}}) n}

更新于2013年4月24日:我现在了解Haskell更多的知识。为了帮助其他人阅读上面提取的代码,我呈现了下面的手写代码,并声称其等效且更易读。我还展示了未被我消除的定义Nat、O、S和nat_rec。

-- Extracted: Natural numbers (non-negative integers)
-- in the manner in which Peano defined them.
data Nat =
   O
 | S Nat
   deriving (Eq, Show)

-- Extracted: General recursion over natural numbers,
-- an interpretation of Nat in the manner of higher-order abstract syntax.
nat_rec :: a1 -> (Nat -> a1 -> a1) -> Nat -> a1
nat_rec f f0 n =
  case n of {
   O -> f;
   S n0 -> f0 n0 (nat_rec f f0 n0)}

-- Given non-negative integers n and m, produce (q, r) with n = q * m + r.
divalg_t :: Nat -> Nat -> (Nat, Nat)
divalg_t n      O = (O, n)      -- n/0: Define quotient 0, remainder n.
divalg_t n (S m') = divpos n m' -- n/(S m')
  where
        -- Given non-negative integers  n and m',
        -- and defining m = m' + 1,
        -- produce (q, r) with n = q * m + r
        -- so that q = floor (n / m) and r = n % m.
        divpos :: Nat -> Nat -> (Nat, Nat)
        divpos n m' = nat_rec (O, O) (incrDivMod m') n
        -- Given a non-negative integer m' and
        -- a pair of non-negative integers (q', r') with r <= m',
        -- and defining m = m' + 1,
        -- produce (q, r) with q*m + r = q'*m + r' + 1 and r <= m'.
        incrDivMod :: Nat -> Nat -> (Nat, Nat) -> (Nat, Nat)
        incrDivMod m' _ (q', r')
          | r' == m'  = (S q', O)
          | otherwise = (q', S r')

2
只是提醒一下,你不必重新发明轮子 :) 你的 ex_t 类型已经在 Coq 中作为 sigT 可用。你可以在这个地址找到所有这个对的变体(因为在构造逻辑中,“存在一个 X 使得 P”只是一个 X 和证明 P 的见证,可能依赖于 X)。 - Vinz
谢谢!我想Sigma类型就像Robert Harper在《编程语言实践基础(草稿版)》中所写的那样吧?“依赖积类型Σ u::κ1.κ2,分类对⟨c1,c2⟩,使得c1 :: κ1,正如预期的那样,而c2 :: [c1/u]κ2,在其中第二个组件的类型对第一个组件本身敏感,而不仅仅是它的类型。” - minopret
你能否在帖子中加入divalgdivalg_t?我认为这会非常有用。 - OrenIshShalom

4

2012年7月25日的《软件基础》副本,在后期章节“Extraction2”中简明地回答了这个问题。答案是可以做到的,就像这样:

Extraction Language Haskell
Extraction "divalg.hs" divalg

还需要一个技巧。divalg必须是Type,而不是Prop。否则,在提取的过程中它将被删除。
哦,@Anthill是正确的,我没有回答问题,因为我不知道如何解释Prof. Pierce在他的NormInType.v变体中如何实现这一点的。
好吧,这是我部分回答的其余部分。
在上面出现“divalg”的地方,需要提供一个以空格分隔的命题列表(每个命题都必须重新定义为Type而不是Prop),这些命题是divalg依赖的。对于一个彻底、有趣且可行的证明提取示例,可以查看上面提到的Extraction2章节。该示例提取到OCaml,但将其适应于Haskell只需使用如上所述的Extraction Language Haskell
部分原因是我花了一些时间不知道上面的答案,是因为我一直在使用2011年下载的日期为2010年10月14日的Software Foundations副本。

2
尊敬的法官,反对意见 - 答案就是问题,而问题本身并不能回答它 ;) - Anthill
1
不要删除你的问题。自己回答问题是可以的,而且保留问题更好。 - Ptival
2
在今年的俄勒冈编程语言暑期学校上,本杰明·皮尔斯发表了一两场关于使用Coq提取能力的演讲。他的演讲视频在这里,http://www.cs.uoregon.edu/Research/summerschool/summer12/curriculum.html 滚动到底部查看皮尔斯的演讲视频。我认为在最后两个视频中的一个中,他谈到了一些有关提取及其涉及的内容。 - Dan Feltey
@Ptival 谢谢,我不会删除。我发现我还没有遵循第二章“提取”的示例。之前我以为我可能是唯一一个仍在使用旧版下载文本的人,根本没有“提取2”这一章节! - minopret

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