Answers:
在实践上,合同就是断言。它们使您可以检查(无量化)程序单个执行的属性。合同检查的核心思想是怪罪-基本上,您想知道谁是违反合同的错。这可以是实现(不计算承诺的值)或调用方(向函数传递错误类型的值)。
关键的见解是,您可以在域理论的逆极限构造中使用与嵌入-投影对相同的机制来跟踪责任。基本上,您从使用断言切换为使用成对断言,其中一对断言归咎于程序上下文,而另一个则归咎于程序。然后,这可以让您用协定包装高阶函数,因为您可以通过交换断言对来建模函数空间的矛盾。(例如,参见Nick Benton的论文“ Undoing Dynamic Typing”。)
从属类型是类型。类型指定用于断言某些程序是否可接受的规则。结果,它们不包含诸如怪罪之类的东西,因为它们的功能是首先防止行为不端的程序存在。没什么可怪的,因为只有格式正确的程序甚至是语法话语。实用上,这意味着使用从属类型来用量词来表达术语的属性非常容易(例如,一个函数可用于所有输入)。
这两种观点并不相同,但是它们是相关的。基本上,关键是合同是从价值的通用域开始的,并使用合同来缩减成本。但是,当我们使用类型时,我们会尝试预先指定较小的值域(具有所需的属性)。因此,我们可以通过类型导向的关系族(即逻辑关系)将两者联系起来。例如,请参见艾哈迈德(Ahmed),芬德勒(Findler),锡克(Siek)和沃德勒(Wadler)的最新著作《全民责备》,或雷诺兹(Reynolds)的《类型的意义:从内在语义到外在语义》。
类型和契约攻击的(相当抽象的)问题是“如何确保程序具有某些属性?”。在能够表达更广泛的属性类别与能够检查程序是否具有属性之间存在着内在的张力。类型系统通常确保非常特定的属性(程序绝不会以某些方式崩溃)并具有类型检查算法。另一方面,合同允许您指定非常广泛的属性(例如,该程序的输出是质数),但不附带检查算法。
但是,没有合同检查算法(始终有效)这一事实并不意味着几乎没有合同检查算法(在实践中往往有效)。我建议您查看Spec#和Frama-C的Jessie插件。他们俩都通过验证条件生成,以一阶逻辑的形式将“该程序遵守该合同”表示为工作,然后询问SMT求解器去尝试查找证明。如果求解器无法找到证明,则程序是错误的,或者,求解器无法找到存在的证明。(这就是为什么这是“几乎”合同检查算法的原因。)还有一些基于符号执行的工具,这大致意味着“该程序遵守此合同”被表达为一堆命题(在某种逻辑上)。参见,例如jStar。
弗拉纳根(Flanagan)的工作试图从两个方面汲取最大的益处,以便您可以快速检查类似类型的属性,然后为其余部分工作。我对混合类型不是很熟悉,但是我确实记得作者说过他的动机是想出一种需要更少注释的解决方案(比他以前在ESC / Java上所做的工作)。但是,从某种意义上说,ESC / Java(和Spec#)中的类型和契约之间也存在一些松散的集成:检查契约时,告诉求解器成功进行类型检查,以便它可以获取该信息。
可以静态检查合同。如果您看一下Dana Xu在ESC / Haskell上的老著作,她能够在编译时实现完全合同检查,仅依靠定理证明者进行算术运算即可。如果我没记错的话,终止可以通过一个简单的深度限制来解决:
合同和类型都允许您在函数上表示Hoare风格(前后条件)规范。两者都可以在编译时静态检查,也可以在运行时动态检查。
依赖类型允许您在类型系统中编码非常广泛的属性,这是合同程序员期望的属性类型。这是因为它们可以取决于类型的值。尽管我相信您引用的论文着眼于替代方法,但是从属类型有被静态检查的趋势。
最终,差异不大。我认为,依赖类型是可以表达规范的逻辑,而契约是可以表达规范的编程方法,这更多。