【发布时间】:2015-08-13 14:06:19
【问题描述】:
对我来说,Nim 最有趣的功能之一是 not nil 注释,因为它基本上允许在编译器的帮助下完全排除各种 NPE / 访问冲突错误。但是,我在实践中很难使用它。让我们考虑一个最基本的用例:
type
SafeSeq[T] = seq[T] not nil
这里的一个直接陷阱是,即使实例化这样的SafeSeq 也不是那么容易。尝试
let s: SafeSeq[int] = newSeq[int](100)
失败并出现错误cannot prove 'newSeq(100)' is not nil,这令人惊讶,因为人们可能认为newSeq 根本不为零。一种解决方法似乎是使用这样的助手:
proc newSafeSeq*[T](size: int): SafeSeq[T] =
# I guess only @[] expressions are provably not nil
result = @[]
result.setlen(size)
下一个问题出现在尝试使用SafeSeq 做某事时。例如,人们可能期望当您映射到 SafeSeq 时,结果不应再次为 nil。但是,这样的事情也会失败:
let a: SafeSeq[int] = @[1,2,3]
let b: SafeSeq[string] = a.mapIt(string, $it)
一般的问题似乎是,一旦返回类型变成普通的seq,编译器似乎就会忘记not nil 属性并且无法再证明它。
我现在的想法是引入一个小的(可以说是丑陋的)辅助方法,让我能够实际证明not nil:
proc proveNotNil*[T](a: seq[T]): SafeSeq[T] =
if a != nil:
result = a # surprise, still error "cannot prove 'a' is not nil"
else:
raise newException(ValueError, "can't convert")
# which should allow this:
let a: SafeSeq[int] = @[1,2,3]
let b: SafeSeq[string] = a.mapIt(string, $it).proveNotNil
但是,编译器在这里也无法证明not nil。我的问题是:
在这种情况下我应该如何帮助编译器推断
not nil?此功能的长期目标是什么,即是否有计划使推断
not nil更强大?手册proveNotNil的问题在于它可能不安全,并且与编译器负责证明它的想法背道而驰。但是,如果proveNotNil仅在极少数情况下才需要,那也不会造成太大影响。
注意:我知道 seq 试图成为 nil 不可知论者,即,即使在 nil 情况下,一切正常。然而,这只适用于在 Nim 中。在连接 C 代码时,nil-hiding-principle 成为错误的危险来源,因为 nil 序列仅在 Nim 端是无害的......
【问题讨论】:
-
我猜标准库需要在那里做一些工作,例如注释
newSeq返回一个not nilseq
标签: nim-lang