“证明是程序;它证明的公式是程序的类型”


37

这可能是哲学上的问题,但我相信对此有一个客观的答案。

如果您阅读有关Haskell的维基百科文章,则可以找到以下内容:

该语言源于Haskell Curry及其知识分子后代的观察,即“证明是程序;它证明的公式是程序的类型”。

现在,我要问的是:这真的不适用于几乎所有的编程语言吗?Haskell的哪些功能(或一组功能)使其符合此声明?换句话说,此语句影响语言设计的主要方式是什么?


4
有人在乎解释为什么要“封闭”投票吗?

1
@Grigory Javadyan:我没有投票赞成关闭,但这可能是因为该问题对于SO来说是格外重要的-哲学上的问题,客观上可以回答或以其他方式回答,在这里通常不合适。但是,在这种情况下,我认为这是合理的,因为答案对于Haskell的实际使用方式具有深远的实际意义。

2
@Grigory:如果这个问题是一个真正的解决方案(在代码中)真正的问题,那么它可以继续使用。投票决定关闭并转到“程序员”。

9
补充一点是,因为我有点儿精疲力尽了-这些问题的答案比不上CS的辛勤研究,因此从某种意义上说,它比90%的SO更“客观”。此外,对于一系列既不是主观的又不是主题的真正的编程问题,sixlettervariable的标准(该解决方案需要代码)非常狭窄。我真的很希望看到包容
主义

2
我对这最终会导致什么产生矛盾,主要是因为我不清楚在Programmers.SE与SO上应该包含哪种内容。但是我要说程序员在多个地方被描述为是针对“主观问题”的,而这个问题显然不是。我的回答是尽可能的非正式和波折,我仍然可以轻松引用其中的大部分内容,即使是严谨的Wikipedia编辑也可以接受。
加利福尼亚州麦肯

Answers:


38

基本概念以某种方式普遍适用,是的,但很少以有用的方式应用。

首先,从类型理论的角度出发,这假定“动态”语言最好被视为具有单一类型,该类型包含(除其他事项外)有关程序员看到的价值性质的元数据,包括这些动态语言将调用的内容一个“类型”本身(从概念上讲,这是不一样的)。任何此类证明都可能没有意思,因此,该概念与静态类型系统的语言最相关。

另外,在这种情况下,许多据称具有“静态类型系统”的语言在实践中必须被视为动态的,因为它们允许在运行时检查和转换类型。特别是,这意味着任何内置有默认支持“反射”等的语言。例如C#。

Haskell期望类型提供多少信息是不同寻常的-特别是,函数不能依赖除指定为其参数的值以外的任何值。另一方面,在具有可变全局变量的语言中,任何函数都可以(潜在地)检查这些值并相应地更改行为。因此具有类型的Haskell函数A -> B可以被认为是一个A隐含的微型程序证明B; 在许多其他语言中的等效函数只会告诉我们,A范围内的任何全局状态都暗示B

请注意,尽管Haskell确实支持诸如反射和动态类型之类的东西,但必须在函数的类型签名中指示此类功能的使用;同样使用全局状态。默认情况下都不可用。

办法通过让运行时异常,或者使用编译器提供的非标准基本操作,打破东西在Haskell为好,如,但那些都具有较强的预期,他们将只能与充分的理解的方式,韩元用于” t破坏外部代码的含义。从理论上讲,其他语言也可以这样说,但是在实践中,大多数其他语言在没有“作弊”的情况下完成任务更加困难,并且对“作弊”的皱眉也更少。当然,在真正的“动态”语言中,整个过程仍然无关紧要。

与在Haskell中相比,该概念可以更进一步。


请注意,尽管异常可以完全集成到类型系统中。
gardenhead

18

您是正确的,Curry-Howard对应关系很普遍。值得熟悉一下它的历史:http : //en.wikipedia.org/wiki/Curry-Howard_correspondence

您会注意到,按照最初的表述,这种对应关系一方面特别适用于直觉逻辑,另一方面适用于简单类型的Lambda演算(STLC)。

经典的Haskell-'98或更早的版本,与STLC紧密相关,并且在大多数情况下,Haskell中的任何给定表达式与STLC中的相应术语之间都有非常简单,直接的翻译(扩展了递归和一些原始类型)。因此,这使库里-霍华德非常明确。如今,由于有了扩展,这样的翻译变得有些棘手。

因此,从某种意义上讲,问题是为什么Haskell如此直接地“退出” STLC。我想到两件事:

  • 类型。与Scheme一样,Scheme也是一种加糖的lambda演算(除其他外),它是强类型的。这意味着经典的Haskell中不存在术语,根据定义,这些术语在STLC中不能很好地键入。
  • 纯度。同样,与Scheme不同,但与STLC一样,Haskell是一种纯的,参照透明的语言。这很重要。具有副作用的语言可以嵌入到无副作用的语言中。但是,这样做是整个程序的转换,而不仅仅是本地的废止。因此,要获得直接的对应关系,有必要从纯粹的功能语言开始。

与大多数语言一样,Haskell在直接应用Curry-Howard对应关系方面也有一种失败的重要方式。Haskell作为一种图灵完备的语言,包含无限递归的可能性,因此也可以终止。STLC没有定点运算符,图灵不完整,并且正在严格规范化 -也就是说,STLC中术语的任何归约都不会终止。递归的可能性意味着人们可以“欺骗” Curry-Howard。例如,let x = x in x具有类型forall a. a-即,由于它永远不会返回,我可以假装它给了我任何东西!由于我们始终可以在Haskell中执行此操作,因此这意味着我们无法完全“相信”与Haskell程序相对应的任何证据,除非我们有单独的证据证明程序本身正在终止。

Haskell(特别是ML系列)之前的函数式编程沿袭是CS研究的结果,该研究专注于构建语言,您可以轻松地证明(其中包括)其他事物,这些研究非常了解CH,并且源于CH。相反,Haskell既是宿主语言,又是许多正在开发中的证明助手(例如Agda和Epigram)的灵感,这些助手都植根于与CH世系非常相关的类型理论的发展。


1
最好强调一下,非终止以某些方式破坏了证明,尽管从逻辑的观点来看显然是灾难性的,但保留了许多其他特性。特别地,A -> B给定一个的函数A将要么产生B要么什么都不产生。它永远不会产生C,并且B它提供的类型值,或者如果不同,仍然完全取决于所A提供的类型。

@camccann-有点挑剔,但是我要区分底部和“什么都没有”,这更像是Void,不是吗?懒惰使两者都变得越来越复杂。我想说一个函数A -> B 总是产生type的值B,但是该值可能包含的信息少于预期的信息。
sclv 2011年

挑剔很有趣!当我说“没什么”时,我是指在执行评估的上下文中的价值水平,而底层实际上只是作为抽象而存在,而不是有形的东西。被求值的表达式永远不会“看到” bottom的值,只是它不使用的术语(可能是bottom)和它使用的术语(具有非底部值)。从某种意义上说,尝试使用底部“从不发生”是因为尝试这样做会在使用发生之前结束对整个表达式的求值。

12

对于一阶近似,大多数其他(弱类型和/或单类型)语言不支持以下语言之间的严格语言级别划分:

  • 命题(即类型)
  • 一个证明(即一个节目示范我们如何从一组原语和/或其它的构造命题高阶构建体)

以及两者之间的严格关系。如果有的话,其他此类语言所提供的最佳保证是

  • 给定有限的输入约束,以及当时环境中发生的任何事情,我们都可以产生具有有限约束的值。(传统静态类型,请参阅C / Java)
  • 每个构造都是相同的类型(动态类型,参见ruby / python)

请注意,按类型,我们指的是一个命题,因此某些事物描述的信息远比intbool得多。在Haskell中,函数的渗透文化仅受其参数的影响 - 无例外 *。

稍微严格一点,一般的想法是,通过对(几乎)所有程序结构实施严格的直觉方法(即,我们只能证明我们可以构造的),并通过在这种情况下限制原始结构的集合,我们拥有的方式

  • 所有语言原语的严格命题
  • 可以组合原语的有限机制集

Haskell构造倾向于很好地推断其行为。如果我们可以构造一个A隐含的证明(read:function)证明,B则它具有非常有用的属性:

  • 总是成立(只要我们有一个A,我们就可以构造一个B
  • 此含义取决于A,而没有其他内容。

从而使我们能够有效地推理局部/全局不变量。回到原来的问题;Haskell最能促进这种思维方式的语言功能包括:

  • 将效果的纯度/分段分解为显式构造(效果既要考虑也要输入!)
  • Haskell编译器中的类型推断/检查
  • 能够将控制和/或数据流不变式嵌入程序的命题/类型的能力旨在证明:(具有多态性,类型族,GADT等)
  • 参照完整性

所有这些都不是Haskell独有的(其中许多想法都非常古老)。但是,当与标准库中的丰富抽象集(通常在类型类中找到),各种语法级加糖以及对程序设计中的纯净度严格承诺结合在一起时,我们最终会获得一种在某种程度上能够同时兼顾两者的语言对于现实世界的应用程序足够实用,但同时又证明了比大多数传统语言更容易推理的能力。

这个问题值得一个足够深刻的答案,在这种情况下,我不可能做到公正。我建议阅读更多关于维基百科/文献资料:

* NB:我掩饰/忽略了Haskell杂质的某些棘手方面(异常,非终止等),这些方面只会使论点变得复杂。


4

有什么功能?类型系统(是静态的,纯的,多态的)。Wadler的“免费定理”是一个很好的起点。对语言设计有明显影响吗?IO类型,类型类。


0

克莱尼层次结构告诉我们,证明不是程序。

第一个递归关系是:

R1( Program , Iteration )  Program halts at Iteration.
R2( Theorem , Proof ) Proof proves a Theorem.

第一个递归可枚举关系是:

(exists x) R1( Program , x )  Program Halts.
(exists x) R2( Theorem , x)   Theorem is provable.

因此,程序是一个定理,程序暂停的存在迭代就像证明该定理的存在的证明一样。

Program = Theorem
Iteration = Proof

当从规范正确生成程序时,我们必须能够证明它满足规范,并且如果我们能够证明程序满足规范,那么它就是正确的程序综合。因此,如果我们证明程序满足规范,就可以执行程序综合。程序满足规格的定理是指定理是指所合成的程序的程序。

马丁·洛夫(Martin Lof)的错误结论从未产生过任何计算机程序,令人惊讶的是,人们相信这是一种程序综合方法。尚未给出正在合成的程序的完整示例。诸如“输入类型并输出该类型的程序”之类的规范不是功能。有许多这样的程序,并且随机选择一个程序不是递归函数,甚至不是函数。这只是一个愚蠢的尝试,它显示了一个愚蠢的程序,它并不代表计算递归函数的真实计算机程序。


2
这如何回答以下问题:“该声明以什么方式影响了语言的设计?”
gnat 2014年

1
@gnat-这个答案解决了原始问题中的一个基本假设,即:“ doesn't this really apply to pretty much all the programming languages?“这个答案声称/表明该假设是无效的,因此解决基于有缺陷前提的其余问题没有任何意义。
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.