40

好的,所以我意识到我可能会为此后悔一辈子,但是…… Djinn 实际上是如何工作的?

该文档说它使用了一种“LJ 的扩展”的算法,并指出了一篇关于 LJT 的冗长令人困惑的论文。据我所知,这是一个由高度形式化的规则组成的大型复杂系统,用于确定哪些逻辑陈述是真或假。但这甚至没有开始解释如何将类型签名转换为可执行表达式。大概所有复杂的形式推理都以某种方式参与其中,但情况严重不完整。


这有点像我尝试用 BASIC 编写 Pascal 解释器的时候。(别笑!我才十二岁……)我花了好几个小时试图弄清楚,最后我不得不放弃。我只是无法弄清楚你是如何从包含整个程序的巨大字符串中获得的,你可以将其与已知的程序片段进行比较,以便决定实际做什么。

答案当然是你需要写一个叫做“解析器”的东西。一旦你理解了这是什么以及它做了什么,突然间一切都变得显而易见了。哦,编写代码仍然不是一件容易的事,但想法很简单。你只需要编写实际的代码。如果我在十二岁的时候就知道解析器,那么也许我不会花两个小时盯着一个空白屏幕。

我怀疑 Djinn 所做的从根本上说很简单,但我错过了一些重要的细节,这些细节解释了所有这些复杂的逻辑体操与 Haskell 源代码之间的关系......

4

3 回答 3

37

Djinn 是一个定理证明者。您的问题似乎是:定理证明与编程有什么关系?

强类型编程与逻辑有着非常密切的关系。特别是,ML 传统中的传统函数式语言与Intuitionist Propositional Logic密切相关。

口号是“程序就是证明,程序证明的命题就是它的类型”。
一般来说你可以想到

 foo :: Foo

话说那foo是一个公式的证明Foo。例如类型

 a -> b  

对应于函数 from ato b,所以如果你有一个证明a和一个证明a -> b你就有一个证明b。因此,函数完全对应于逻辑中的含义。相似地

(a,b)

对应于合取(逻辑与)。所以逻辑重言式a -> b -> a & b对应于 Haskell 类型 a -> b -> (a,b) 并有证明:

\a b -> (a,b)

这是“和引入规则”,fst :: (a,b) -> asnd :: (a,b) -> b与2“和消除规则”相对应

同样,a OR b对应于 Haskell 类型Either a b

在Haskell CurryWilliam Alvin Howard之后,这种对应有时被称为“Curry-Howard 同构”或“ Curry-Howard 对应

这个故事因 Haskell 中的非全面性而变得复杂。

Djinn“只是”一个定理证明者。

如果您有兴趣尝试编写克隆,“Simple Theorem Prover”的谷歌搜索结果的第一页有这篇论文,它描述了为 LK 编写一个似乎是用 SML 编写的定理证明器。

编辑:至于“如何证明定理是可能的?” 答案是从某种意义上说并不难。这只是一个搜索问题:

考虑将问题重述为:我们有一组我们知道如何证明 S 的命题,以及一个我们想证明 P 的命题。我们该怎么办?首先,我们问:我们是否已经在 S 中证明了 P?如果是,我们可以使用它,如果不是,我们可以在 P 上进行模式匹配

case P of
   (a -> b) -> add a to S, and prove b (-> introduction)
   (a ^ b)  -> prove a, then prove b (and introduction)
   (a v b)  -> try to prove a, if that doesn't work prove b (or introduction)

如果这些都不起作用

for each conjunction `a ^ b` in S, add a and b to S (and elimination)
for each disjunction `a v b` in S, try proving `(a -> P) ^ (b -> P)` (or elimination)
for each implication `a -> P` is S, try proving `a` (-> elimination)

真正的定理证明者有一些聪明,但想法是一样的。“决策程序”的研究领域研究了为某些保证有效的公式寻找证据的策略。另一方面,“战术”着眼于如何以最佳方式对证明搜索进行排序。

至于:“如何将证明翻译成 Haskell?”

形式系统中的每条推理规则都对应着一些简单的 Haskell 构造,所以如果你有一棵推理规则树,你就可以构造一个对应的程序——Haskell 毕竟是一种证明语言。

含义介绍:

\s -> ?

或介绍

Left
Right

和介绍

\a b -> (a,b)

和消除

fst
snd

ETC

augustss 在他的回答中说,他们在 Djinn 中实现这一点的方式对于 SO 的回答来说有点乏味。不过我敢打赌,您可以自己弄清楚如何实现它。

于 2012-04-18T22:00:03.763 回答
24

在最一般的术语中,根据 Curry-Howard 同构,类型和命题以及值和证明之间存在对应关系。Djinn 使用这种对应关系。

更具体一点,假设你想找到一个 Haskell 术语 type (a, b) -> (b, a)。首先,您将类型转换为逻辑语句(Djinn 使用命题逻辑,即没有量词)。逻辑语句去(A and B) is true implies (B and A) is true。下一步就是证明这一点。对于命题逻辑,总是可以机械地证明或反驳一个陈述。如果我们可以反驳它,那么这意味着(终止)Haskell 中没有对应的术语。如果我们可以证明它,那么就有一个该类型的 Haskell 项,而且,Haskell 项具有与证明完全相同的结构。

最后一个语句必须是合格的。您可以选择使用不同的公理和推理规则集来证明该陈述。如果您选择建设性逻辑,则证明和 Haskell 项之间只有对应关系。“正常”,即经典逻辑具有诸如A or (not A)公理之类的东西。那将对应于 Haskell 类型Either a (a -> Void),但是没有这种类型的 Haskell 术语,所以我们不能使用经典逻辑。任何命题陈述都可以在建设性命题逻辑中被证明​​或否定,这仍然是正确的,但这样做比在经典逻辑中涉及的要多得多。

回顾一下,Djinn 将类型转换为逻辑中的命题,然后使用构造逻辑的决策程序来证明命题(如果可能),最后将证明转换回 Haskell 术语。

(在这里说明它是如何工作的太痛苦了,但是给我 10 分钟的时间在白板上,你会一清二楚的。)

作为您思考的最后评论:Either a (a -> Void)如果您有 Scheme 的call/cc. 选择一个更具体的类型,Either a (a -> Int)然后弄清楚如何。

于 2012-04-19T08:57:26.847 回答
4

也许我看错了。也许所有这些形式逻辑的东西只是分散注意力。与其盯着 LJT 或其他什么的扣除规则,也许我应该做的是看看 Haskell。

Haskell 中有,什么,6 种可能的表达方式?每一个都对它使用的变量施加不同的类型约束,对吧?所以,也许我只是为函数类型中的每个参数生成一个新变量,然后开始查看我可以构造什么表达式。

甚至不必在蛮力搜索中生成所有可能的表达式。如果您的参数都没有函数类型,那么尝试函数应用程序是没有意义的。如果您的所有参数都是多态类型变量,case那么表达式对您没有帮助。等等。可用的类型告诉您哪种表达式可能有效。

如果您允许您的代码调用现有的顶级函数,事情会变得更有趣。除了有趣的多态类型的作用域问题之外,还有一个问题是要弄清楚哪些函数会或不会帮助你。

显然,我将不得不离开并考虑一段时间......

于 2012-04-19T11:15:07.587 回答