你能在Clojure中获得加载函数的"代码作为数据"吗?

har*_*rpo 10 reflection clojure

换句话说," 好的,所以代码就是数据...... "

该线程解决了如何从源文件中读取,但我想知道如何将已经加载的函数的s表达式转换为我可以读取和操作的数据结构.

换句话说,如果我说,

(defn example [a b] (+ a b))
Run Code Online (Sandbox Code Playgroud)

我不能在运行时获得该列表?这不是"代码作为数据"的全部意义吗?

这实际上是一个普遍的Lisp问题,但我正在寻找Clojure的答案.

liw*_*iwp 7

您可以使用clojure.repl/source宏来获取符号的来源:

user> (source max)
(defn max
  "Returns the greatest of the nums."
  {:added "1.0"
   :inline-arities >1?
   :inline (nary-inline 'max)}
  ([x] x)
  ([x y] (. clojure.lang.Numbers (max x y)))
  ([x y & more]
   (reduce1 max (max x y) more)))
nil
Run Code Online (Sandbox Code Playgroud)

但这只是答案的一部分.AFAICT source查找定义给定符号的源文件名和行号,然后从文件中打印源代码.因此,source不适用于您没有源代码的符号,即AOT编译的clojure代码.

回到原始问题,您可以将其source视为读取与给定符号关联的元数据并简单地打印它.即它是作弊.它不会以任何方式将"代码作为数据"返回给您,其中代码我的意思是编译的clojure函数.

在我看来,"作为数据的代码"指的是lisps的特性,其中源代码实际上是一个lisp数据结构,因此它可以被lisp阅读器读取.也就是说,我可以创建一个有效的lisp代码的数据结构eval.

例如:

user=> (eval '(+ 1 1))
2
Run Code Online (Sandbox Code Playgroud)

'(+ 1 1)是一个文字列表,由clojure读者读取,然后作为clojure代码进行评估.

更新: Yehonathan Sharvit在其中一条评论中询问是否可以修改函数的代码.下面的代码片段读取函数的源代码,修改生成的数据结构,最后评估导致新函数的数据结构my-nth,定义如下:

(eval
 (let [src (read-string (str (source-fn 'clojure.core/nth) "\n"))]
   `(~(first src) my-nth ~@(nnext src))))
Run Code Online (Sandbox Code Playgroud)

syntax-quote行替换nthmy-nthdefn形式.