尼姆:如何证明不是零?
Nim: How to prove not nil?
对我来说,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
根本不是 nil。解决方法似乎是使用这样的助手:
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)
一般的问题似乎是一旦 return 类型变成普通的 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
序列仅在 Nim 端无害...
使用 isNil 魔术检查 nil:
type SafeSeq[T] = seq[T] not nil
proc proveNotNil[T](s: seq[T]): SafeSeq[T] =
if s.isNil: # Here is the magic!
assert(false)
else:
result = s
let s = proveNotNil newSeq[int]()
对我来说,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
根本不是 nil。解决方法似乎是使用这样的助手:
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)
一般的问题似乎是一旦 return 类型变成普通的 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
序列仅在 Nim 端无害...
使用 isNil 魔术检查 nil:
type SafeSeq[T] = seq[T] not nil
proc proveNotNil[T](s: seq[T]): SafeSeq[T] =
if s.isNil: # Here is the magic!
assert(false)
else:
result = s
let s = proveNotNil newSeq[int]()