开发者

Lists for S-expressions, Vectors for (literal) data?

I've noticed my own habit of using vectors much more often than lists when I need a literal to test a function that takes a sequence.

I.e. :

(map inc [1 2 3])

But not:开发者_如何学JAVA

(map inc (list 1 2 3))

Although they are both correct (and the few extra characters in the second one can be solved with quoting).

So, I practically use lists only to construct s-expressions, whether I do it manually or in a macro.

Not taking into consideration the performance issues, which was already discussed here, which would you consider a better coding style?

I know it is a bit subjective question, but it occurred to me that maybe I'm carrying over my "code and data separation" mindset from my experience in non-lisp languages.


Vector literals, when performance is no issue, are generally preferred to lists; firstly, they're easier to type, and less characters, but (probably) more importantly: they're easier to read. (easier to differentiate from function calls, etc.)

In fact, I'm pretty sure the ease of readability gained from vector literals is the reason for their enforcement in many macros.

(let (a 1) a) ;; => Exception: let requires a vector for it's binding

(let [a 1] a) ;; => 1


A vector in Clojure vaguely means "this is data". The group of bindings for a let or fn is just that, data. It's a sequence of symbols, or sequence of symbol-value pairs.

A list vaguely means "This is a function or macro call". When you see a list, you'll be pretty safe assuming the first thing in that list is something callable, and the rest of the things in the list are arguments to it.

There are exceptions. You'll see both (ns foo (:use (bar baz))) and (ns foo [:use [bar baz]]) in idiomatic code. Not sure why other than that Clojure grew very quickly and semi-organically in some areas and is still growing. But it's a good rule of thumb.

Vectors don't need to be quoted, which is a good way to avoid certain bugs, especially given that so many things in Clojure are callable as functions. (:foo :bar) is not a list of two keywords, but it'll compile, calling :foo as a keyword lookup on the keyword :bar, evaluating as nil. There's no way to screw up [:foo :bar]. It's always a vector of two keywords.

The benefit of not having to quote the elements of a vector should not be understated. How would you like to write this with lists?

(let [x 123 y 456] 
  [[:foo x] [:bar y]])

One way is so verbose that your data is lost in a forest of list. The other way is an unnecessary mess of punctuation.

(let [x 123 y 456] 
  (list (list :foo x) (list :bar y)))

(let [x 123 y 456] 
  `((:foo ~x) (:bar ~y)))

Clojure is perhaps less of a parenthesis soup than other Lisp dialects, thanks to [] and {}. Though some people dislike the ))])}) that Clojure ends up having, I think it's preferable to losing yourself in a long string of )))))))).

"Code is data" doesn't mean that all of your data has to look like all of your code and vice versa. You have access to code-as-data when you need it, which is rarely, usually in macros. In the meantime, if your data is not code, why should it look like code? A sequence of phone numbers is not code. There's no reason it has to be in a list.

And as you said, there are performance issues with lists.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜