为什么Clojure习惯用法更喜欢返回nil而不是Scheme这样的空列表?

Why Clojure idiom prefer to return nil instead of empty list like Scheme?

从另一个问题的评论中,有人说Clojure惯用语更喜欢返回nil而不是Scheme中的空列表。 这是为什么?

喜欢,

1
(when (seq lat) ...)

代替

1
2
  (if (empty? lat)  
    '() ...)


我可以想到一些原因:

  • 逻辑上的区别。在Clojure中,无表示无/无价值。而'()"空列表是一个值-它恰好是一个值,它是一个空列表。在概念上和逻辑上,区分两者通常很有用。

  • 适合JVM-JVM对象模型支持空引用。而且很多Java API返回null表示"无"或"未找到值"。因此,为确保轻松的JVM互操作性,Clojure以类似的方式使用nil是有意义的。

  • 懒惰-此处的逻辑非常复杂,但是我的理解是,对Clojure的惰性序列使用nil表示"无列表"效果更好。由于默认情况下Clojure是一种惰性函数式编程语言,因此使这种用法成为标准是有意义的。有关更多说明,请参见http://clojure.org/lazy。

  • "虚假"-在编写检查集合的条件代码时使用nil表示"无",也表示" false"很方便-因此您可以编写(if (some-map :some-key) ....)之类的代码来测试哈希映射是否包含给定键的值。

  • 性能-测试nil比检查列表是否为空更有效...因此,将此习语用作标准可以导致更高性能的惯用代码

请注意,Clojure中仍有一些函数确实返回一个空列表。一个例子是休息:

1
2
(rest [1])
=> ()

关于休息与下一个的问题将详细说明为什么会这样。


还要注意,集合类型和nil的并集构成一个monoid,而将monoid加和nil则与monoid 0串联在一起。因此,nil会将空列表语义保留在串联之下,同时还表示错误或"丢失"的值。

Python是另一种语言,其中常见的monoid身份表示错误的值:0,空列表,空元组。


摘自Clojure的喜悦

Because empty collections act like true in Boolean contexts, you need an idiom for testing whether there's anything in a collection to process. Thankfully, Clojure provides such a technique:

1
2
3
4
5
(seq [1 2 3])
;=> (1 2 3)

(seq [])
;=> nil

在其他Lisps中,例如Common Lisp,空列表用于表示nil。这称为nil punning,仅在空列表为falsey时才可行。在这里返回nil是Clojure重新引入nil punning的方法。


自从我写了评论以来,我会写一个答案。 (skuro的答案提供了所有信息,但可能太多了)

  • 首先,我认为应该首先考虑更多重要的事情。
  • seq是每个人大部分时间使用的东西,但是empty?恰好是(not (seq lat))
  • 在Clojure中,'()为true,因此通常情况下,如果序列完成,您想返回的内容为false。
  • 如果您的分支中只有一个importend分支,而另一个分支返回false /'()或类似的内容,那么为什么要记下该分支。 when仅具有一个分支,如果您希望具有副作用,则特别好。您不必使用do
  • 请参阅以下示例:

    (如果为假
    '()
    (做(println 1)
    (println 2)
    (println 3)))

    你可以写

    (当为true时
    (println 1)
    (println 2)
    (println 3))

    没有那么不同,但是我认为它更好阅读。

    附言

    并不是说有称为if-notwhen-not的函数,它们通常比(if (not true) ...)更好