无法从命名空间中获取随机(doc)

时间:2012-11-25 18:34:58

标签: clojure

我想显示某个命名空间的随机(doc)页面。

我可以得到的随机函数名称:

user=> (rand-nth (keys (ns-publics 'clojure.core)))
unchecked-char

当我尝试将其传递给(doc)时,我得到了这个:

user=> (doc (rand-nth (keys (ns-publics 'clojure.core))))
ClassCastException clojure.lang.PersistentList cannot be cast to clojure.lang.Symbol clojure.core/ns-resolve (core.clj:3883)

我是Clojure的新手,我不知道如何处理这个...我试图将其转换为regexp并使用(find-doc)但也许有更好的方法来做到这一点......

1 个答案:

答案 0 :(得分:5)

<强>解释

这里的问题是doc是一个宏,而不是一个函数。您可以使用repl中的source宏进行验证。

(source doc)

; (defmacro doc
;   "Prints documentation for a var or special form given its name"
;   {:added "1.0"}
;   [name]
;   (if-let [special-name ('{& fn catch try finally try} name)]
;     (#'print-doc (#'special-doc special-name))
;     (cond
;       (special-doc-map name) `(#'print-doc (#'special-doc '~name))
;       (resolve name) `(#'print-doc (meta (var ~name)))
;       (find-ns name) `(#'print-doc (namespace-doc (find-ns '~name))))))

如果你是Clojure(和lisps)的新手,你可能还没有遇到过宏。作为一个毁灭性的简要解释,函数在评估的代码上运行,宏在未评估的代码上运行 - 即源代码本身。

这意味着当您键入

(doc (rand-nth (keys (ns-publics 'clojure.core))))

doc尝试对实际的代码行进行操作 - (rand-nth (keys (ns-publics 'clojure.core))) - 而不是评估结果(返回的符号)。代码只不过是Clojure中的一个列表,这就是错误告诉你列表不能转换为符号的原因。

<强>解决方案

所以,你真正想做的是评估代码,然后在结果上调用doc。我们可以通过编写另一个宏来首先评估你给它的代码,然后将其传递给doc

(defmacro eval-doc
 [form]
  (let [resulting-symbol (eval form)]
   `(doc ~resulting-symbol)))

您可以传递eval-doc个任意形式,然后在将它们传递给doc之前对其进行评估。现在我们很高兴。

(eval-doc (rand-nth (keys (ns-publics 'clojure.core))))

修改

虽然上面的代码在repl中工作得很好,但如果你提前使用编译,你会发现每次都会产生相同的结果。这是因为resulting-symbol语句中的let是在编译阶段产生的。提前编译一次意味着将该值烘焙到.jar中。我们真正想做的是将doc的评估推送到运行时。所以,让我们将eval-doc重写为函数。

(defn eval-doc
  [sym]
  (eval `(doc ~sym)))

这很简单。