Clojure China

Refer 语法的一个问题

#1

刚开始学习 Clojure,今天在用 redis 库 carmine 的时候有个语法不太明白:

https://github.com/ptaoussanis/carmine 中:

(ns my-app
  (:require [taoensso.carmine :as car :refer (wcar)]))

其中的 :refer (wcar) 是什么意思呢?一般如果要引用一组函数不是应该用 vector 么,写成 :refer [wcar foo bar] 这样么

我试了下把圆括号改成方括号貌似也能正常运行,:refer [wcar],两者难道是等同的?(wcar) 看着像是调用了 wcar 函数呀

#2

ns 是一个 macro,不会立即执行里面的 (wcar)

1赞
#3

如xhh所言,ns是宏。我们用宏展开你的ns

(macroexpand-1 '(ns my-app
                  (:require [taoensso.carmine :as car :refer (wcar)])))
;;=>
(do (clojure.core/in-ns (quote my-app))
    (clojure.core/with-loading-context
      (clojure.core/refer (quote clojure.core))
      (clojure.core/require
       (quote [taoensso.carmine :as car :refer (wcar)]))) ;; 注意
    (if (.equals (quote my-app) (quote clojure.core))
      nil
      (do (clojure.core/dosync
           (clojure.core/commute
            (clojure.core/deref (var clojure.core/*loaded-libs*))
            clojure.core/conj
            (quote my-app)))
          nil)))

这里(quote [taoensso.carmine :as car :refer (wcar)])'(0 (1) (nil))是一个道理,单独执行(0 (1) (nil))肯定会错,但是quote之后,就会递归地quote其中每个元素,所以不会立即执行(wcar)。此时从语义上来讲,(= '() [])是等价的。

2赞
#4

感谢楼上两位。

顺藤摸瓜看了下源代码:

(foo bar)[foo bar] 这两种写法在宏展开后,前者变成了一个 symbol list,后者变成一个 symbol vector,也就是楼上所说的。

然后 require 函数中调用了 load-libs 函数,load-libs 中用 doseq 来处理这些参数,因为 list 和 vector 都是 sequential collection,所以在这是等同的。

我又验证了下用 #{foo bar} 这种形式会抛出异常,因为 set 不是 sequential 的。