原谅设计非常糟糕的伪代码,但我希望它能解决这个问题:
float sqrt takes float n : [0,inf)
// fancy algorithm
return result
void main
x = sqrt -1 // Compilation error
y = sqrt float.max // This works
z = (y + 1) * (y + 1) // Compilation error (this would result in overflow)
在编译期间,编译器会分析sqrt
函数并将其表征为
sqrt : [0,float.max] -> [0, sqrt float.max[
通过对+, -, *, /
运算符执行相同操作来实现此目的。
在main
函数中,第一个语句无法编译,因为sqrt
不接受负输入。第三个语句不编译,因为*
运算符只接受会导致[float.min, float.max]
输出的输入。
答案 0 :(得分:6)
您正在寻找的是COQ,Agda和Idris等语言,其中类型系统允许通过值对类型进行参数化。 (依赖类型)
这并不像您想象的那么容易,并且通常需要完整的证据(在通话方面)才能满足条件。在你的情况下,你必须证明sqrt的参数不能是负数。
的情况很简单sqrt(5)
但在以下情况下并不那么容易:
sqrt(some_long_computation_on_floats(f))
您需要在此证明some_long_computation_on_floats
只返回正数。可能会或可能不会。
答案 1 :(得分:3)
您可能希望阅读design by contract。特别是,有许多语言和语言扩展支持在代码中设置复杂的合同,其中一些可以在编译时检查。一个简单的例子是JML - 事实上,this paper about JML的第一页有以下sqrt
合同:
//@ requires x >= 0.0;
//@ ensures JMLDouble.approximatelyEqualTo(x, \result * \result, eps);
public static double sqrt(double x) {
/*...*/
}
确实有一些工具可以在不运行代码的情况下检查合同违规情况,例如ESC/Java。
现在,按合同设计实际上非常受欢迎,因为静态类型语言中的类型声明实际上是一种合同形式。然而,更复杂的合同远没那么受欢迎,我会说因为: