如何在Clojure的REPL中显示函数的定义?

40

我希望您能够在REPL中打印出函数的当前定义。有没有什么方法可以实现这个功能?

例如,给定以下代码:

(defn foo [] (if true "true"))

我想说类似这样的话

(print-definition foo)

并获得类似以下的东西

(foo [] (if true "true"))

打印。

5个回答

22

在REPL使用时,除了查看在.clj文件中定义的函数之外,source的替代方法是clojure.repl/source(从1.2.0开始应该可以使用)。如果您使用的是1.1.0或更低版本,则sourceclojure.contrib.repl-utils中。

(defmacro defsource
  "Similar to clojure.core/defn, but saves the function's definition in the var's
   :source meta-data."
  {:arglists (:arglists (meta (var defn)))}
  [fn-name & defn-stuff]
  `(do (defn ~fn-name ~@defn-stuff)
       (alter-meta! (var ~fn-name) assoc :source (quote ~&form))
       (var ~fn-name)))

(defsource foo [a b] (+ a b))

(:source (meta #'foo))
;; => (defsource foo [a b] (+ a b))
一个简单的打印定义:
(defn print-definition [v]
  (:source (meta v)))

(print-definition #'foo)

#' 是一个 读取宏,将 #'foo 扩展为 (var foo)

# 注意:本翻译中的特殊字符(如引号、尖括号等)已进行了 HTML 转义,若需要在 HTML 中使用,请自行替换为相应的字符。
(macroexpand '#'reduce)
;; => (var reduce)

最好使用drop head来摆脱defsource关键字。(alter-meta!(var〜fn-name)assoc:source(drop 1(quote〜&form)) - haijin

18
您需要导入repl名称空间,并使用它的source函数:
(ns myns
    (:use [clojure.repl :only (source)]))
(defn foo [] (if true "true"))
(source foo)

=> (foo [] (if true "true"))
    nil

虽然这种方法在REPL中不起作用,只有在定义函数的.clj文件位于类路径上时才有效。这并没有回答你的问题:你需要有一个defn函数,可以存储在它定义的fn元数据中,函数来源的信息。然后你可以编写一个函数来调用那部分元数据。这应该不会太困难。


14

Clojure没有反编译器,这意味着除非从磁盘加载了defn,否则无法获取任意函数的源代码。但是,您可以使用一个称为serializable-fn的巧妙技巧来创建一个函数,该函数的源代码存储在其元数据中:http://github.com/Seajure/serializable-fn

defsource答案与此非常相似,但此解决方案适用于任意fns,而不仅仅是顶级defns。它还使fns在repl上漂亮地打印,而无需特殊的打印函数。


11
在clojure 1.2的REPL中,source函数是立即可用的。您可以这样使用它:
$ java -cp clojure.jar clojure.main
Clojure 1.2.0
user=> (source slurp)
(defn slurp
  "读取指定名称为f的文件,使用编码enc将其作为字符串读入并返回。"
  {:added "1.0"}
  ([f & opts]
     (let [opts (normalize-slurp-opts opts)
           sb (StringBuilder.)]
       (with-open [#^java.io.Reader r (apply jio/reader f opts)]
         (loop [c (.read r)]
           (if (neg? c)
             (str sb)
             (do
               (.append sb (char c))
               (recur (.read r)))))))))
nil
user=>
REPL的user命名空间还会自动导入clojure.repl库中的其他一些函数。在此处查看API文档:here
但是,正如其他答案在这里指出的那样,您不能直接使用source来打印您在REPL中定义的函数。

5

最近我在Clojure邮件列表上问了这个问题,答案包括覆盖REPL的部分以将输入(和输出)存储起来以供将来参考,以及覆盖defn以将源代码存储在元数据中(然后可以在REPL中轻松检索)。

在Clojure邮件列表上阅读该主题


-1,如果您在此处提供更详尽的解释,参见meta,那么这个答案可能会更好。这个答案并没有提供具体的建议。 - Brad Koch

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接