在我向不熟悉 clojure 的同事解释一些 clojure 代码之前,我从来没有真正想过这个问题。let
当他问为什么你使用向量来声明绑定而不是列表时,我正在向他解释。我真的没有给他答案。但是该语言确实限制您使用列表:
=> (let (x 1) x)
java.lang.IllegalArgumentException: let requires a vector for its binding (NO_SOURCE_FILE:0)
为什么会这样?
我想主要是可读性。每当在 Clojure 中需要绑定时,都非常一致地使用向量。很多人都同意绑定向量使事情变得更好,并且更容易辨别绑定是什么以及正在运行的代码是什么。
只是为了好玩:
user=> (defmacro list-let [bindings & body] `(let ~(vec bindings) ~@body))
#'user/list-let
user=> (macroexpand-1 '(list-let (x 0) (println x)))
(clojure.core/let [x 0] (println x))
user=> (list-let (x 0 y 1) (println x y))
0 1
nil
这是来自 Scheme 的一个成语。在许多 Scheme 实现中,方括号可以与列表文字中的圆括号互换使用。在那些 Scheme 实现中,方括号通常用于将参数列表、参数列表和绑定与 S 表达式或数据列表区分开来。
在 Clojure 中,圆括号和方括号的含义不同,但它们在绑定声明中的使用方式相同。
Clojure 非常努力地保持一致性。没有技术原因不能在let
, fn
,with-open
等中使用列表表单...事实上,您可以my-let
轻松地创建自己的表单,而不是使用列表表单。然而,除了明显突出之外,该向量在表单中始终如一地使用以表示“这里有一些绑定”。您应该努力在自己的代码中维护这一理想。
我的猜测是这是一个约定
fn
用过,defn
用过,用过loop
。
似乎它适用于类似于具有某些参数的代码块的所有内容;更具体地说,方括号用于标记这些参数
其他形式的代码块不要使用它,比如if
or do
。他们没有任何参数
另一种思考方式let
是简单地从 lambda 派生。这两个表达式是等价的:
((fn [y] (+ y 42)) 10)
(let [y 10] (+ 42 y))
因此,作为学术或教学点,您甚至可以编写自己的非常基本的版本,let
其中包含一个列表和一个向量:
(defmacro my-let [x body]
(list (list `fn[(first x)]
`~body)
(last x)))
(my-let (z 42) (* z z))
尽管没有实际理由这样做。