在 Clojure 中是否有惯用的替代方法来替代 nil-punning?

Is there an idiomatic alternative to nil-punning in Clojure?

我正在阅读一些 Clojure 代码,其中有一堆未初始化的值,如 nil 用于传递记录中的数值。

现在有很多 Clo​​jure 库 treat this as idiomatic。这意味着它是一个公认的约定。

但它也会导致 NullPointerException,因为并非所有 Clojure 核心函数都可以处理 nil 作为输入。 (他们也不应该)。

其他语言有 MaybeOption 的概念来代理值为 null 的值,作为减轻 NullPointerException 风险的一种方式。这在 Clojure 中是可能的 - but not very common

您可以使用 fnil 做一些小技巧,但它并不能解决所有问题。

另一种方法是将未初始化的值简单地设置为像:empty-value这样的符号,以强制用户在所有处理代码中显式处理这种情况。但这并不是 nil 的真正重大进步 - 因为直到 运行 时间你才真正发现所有场景(在其他人的代码中)。

我的问题是:Clojure 中是否有 nil-punning 的惯用替代方法?

不确定您是否在 nil-punning 上阅读过这个 lispcast post,但我确实认为它很好地说明了为什么它是惯用的并且涵盖了我没有考虑的各种重要考虑因素请参阅其他 SO 问题中提到的内容。

基本上,nil 是 clojure 中的 first-class。尽管它具有固有的传统含义,但它 一个适当的值,并且可以在许多上下文中以 context-dependent 的方式被这样对待。这使得它比宿主语言中的 null 更加灵活和强大。

例如,像这样的东西甚至不会在 java 中编译:

if(null) {
....
}

在 clojure 中,(if nil ...) 可以正常工作。所以很多情况下你可以安全的使用nil。我还没有看到一个 java 代码库没有 乱七八糟的 到处都是像 if(foo != null) { ... 这样的代码。也许 java 8 的 Optional 会改变这一点。

我认为您 可以 运行 很容易陷入问题的地方是 java 处理实际 null 的互操作场景.在许多情况下,一个好的 clojure 包装器库也可以帮助您避免这种情况,这是 更喜欢 一个而不是直接 java 可能的互操作的一个很好的理由。

鉴于此,您可能想要 re-consider 抗击这股潮流。但是既然你问的是替代品,我认为这是一个很棒的选择:prismatic's schema. Schema has a Maybe schema (and many other useful ones as well), and it works quite nicely in many scenarios. The library is quite popular and I have used it with success. FWIW, it is recommended in the recent clojure applied book.

在 Clojure 中是否有 nil-punning 的惯用替代方法?

没有。正如 所解释的那样,nil-punning 是惯用的。但它不像 Common Lisp 那样普遍,在 Common Lisp 中(有人告诉我)空列表等同于 nil。

Clojure 曾经以这种方式工作,但处理列表的 CL 函数通常对应于处理序列的 Clojure 函数。并且这些序列可能是惰性的,因此将惰性序列与其他序列统一起来是有好处的,这样可以保留任何惰性。我认为这种演变发生在 Clojure 1.2 之后。 Rich 详细描述了它 here

如果您需要 option/maybe 类型,请查看 core.typed 库。与 Prismatic Sc​​hema 不同,它在编译时运行。