子集类型和非空
表单的子集类型定义
type MySubset = x: BaseType | RHS(x)
MySubset
作为一种类型引入,它代表满足布尔表达式x
的类型值。由于 Dafny 中的每个类型都必须是非空的,因此有义务证明您声明的类型具有某些成员。Dafny 可能会找到一些候选值,并会尝试查看其中是否有任何一个满足。如果候选人不这样做,您会收到与您看到的一样的错误消息。有时,错误消息会告诉您 Dafny 尝试了哪些候选值。BaseType
RHS(x)
RHS
在您的情况下,Dafny 尝试的唯一候选值是 value null
。正如詹姆斯指出的那样,该值null
甚至没有达到一垒,因为BaseType
在您的示例中是一种非空引用。如果您更改binRelOnS<T>
为binRelOnS?<T>
,则null
有可能成为证明您的子集类型为非空的证人。
用户提供的证人
由于 Dafny 不太擅长提出候选证人,因此您可能必须自己提供一个。为此,您可以witness
在声明的末尾添加一个子句。例如:
type OddInt = x: int | x % 2 == 1 witness 73
由于73
满足 RHS 约束x % 2 == 1
,Dafny 接受这种类型。在某些程序中,您心目中的见证可能仅在幽灵代码中可用。然后,您可以编写ghost witness
而不是witness
,这允许后续表达式是幽灵。幽灵见证可以用来让 Dafny 验证器相信一个类型是非空的,但它并不能帮助 Dafny 编译器初始化该类型的变量,因此您仍然需要自己初始化这些变量。
使用witness
子句,您可以尝试使用您对子集类型的原始定义来提供您自己的见证func
。但是,witness 子句采用表达式,而不是语句,这意味着您不能使用new
. 如果您不关心编译程序并且愿意相信见证人的存在,您可以声明一个承诺返回合适见证人的无正文函数:
type MySubset = x: BaseType | RHS(x) ghost witness MySubsetWitness()
function MySubsetWitness(): BaseType
ensures RHS(MySubsetWitness())
您将需要ghost witness
或function method
. 该函数MySubsetWitness
将永远没有主体,因此您有可能在某些值满足方面犯错RHS
。
该witness
子句是在 Dafny 版本 2.0.0 中引入的。2.0.0 发行说明提到了这一点,但显然没有给出太多解释。如果您想查看更多示例,请在Dafny 测试套件witness
中搜索该关键字。
类的子集类型
在您的示例中,如果您将基本类型更改为可能为空的引用类型:
type func<T> = f: binRelOnS?<T> | f.Valid() && f.isFunction()
那么您的下一个问题将是 RHS 取消引用f
。您可以通过削弱子集类型约束来解决此问题,如下所示:
type func<T> = f: binRelOnS?<T> | f != null ==> f.Valid() && f.isFunction()
现在到了可能会破坏交易的部分。子集类型不能依赖于可变状态。这是因为类型是一个非常静态的概念(与规范不同,规范通常取决于状态)。如果一个值在某个时刻可以满足一个类型,然后在程序中的一些状态更改之后,不满足该类型,那将是一场灾难。(事实上,几乎所有具有子集/细化/依赖类型的类型系统都适用于函数式语言。)因此,如果您的Valid
orisFunction
谓词有一个reads
子句,那么您就无法func
按照您希望的方式进行定义。但是,只要两者Valid
都只isFunction
依赖const
于类中字段的值,那么就不需要reads
子句并且一切就绪。
鲁斯坦