我正在寻找能够让REPL打印出函数的当前定义的能力。有没有办法做到这一点?
例如,给定:
(defn foo [] (if true "true"))
我想说点什么
(print-definition foo)
并获得
的内容(foo [] (if true "true"))
打印。
答案 0 :(得分:20)
source
的替代方法(自clojure.repl/source
起启用REPL时应通过1.2.0
提供。)如果您使用的是1.1.0
或更低,{ {1}}位于source
。)中,供REPL使用,而不是查看clojure.contrib.repl-utils
文件中定义的函数:
.clj
一个简单的(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))
:
print-definition
(defn print-definition [v]
(:source (meta v)))
(print-definition #'foo)
只是reader macro,从#'
扩展到#'foo
:
(var foo)
答案 1 :(得分: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
的元数据中存储函数的来源。然后你会编写一个函数来回忆那些元数据。这应该不是非常困难。
答案 2 :(得分:13)
Clojure没有反编译器,所以这意味着没有办法获取任意函数的来源,除非它是从磁盘加载的defn。但是,您可以使用名为serializable-fn的简洁hack来创建一个将源表单存储在其元数据中的函数:http://github.com/Seajure/serializable-fn
defsource答案与此非常相似,但此解决方案适用于任意fns,而不仅仅是顶级定义。它还使fns在repl上打印得漂亮,没有特殊的打印功能。
答案 3 :(得分:11)
在clojure 1.2的REPL中,source
功能立即可用。你可以这样使用它:
$ java -cp clojure.jar clojure.main Clojure 1.2.0 user=> (source slurp) (defn slurp "Reads the file named by f using the encoding enc into a string and returns it." {: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=>
其他一些函数也会自动从clojure.repl库导入REPL的user
命名空间。请参阅API文档here。
但是,正如在此处的其他答案中指出的那样,您不能使用source
来打印您在REPL中定义的函数。
答案 4 :(得分:4)
我最近在Clojure邮件列表上确切地问了这个问题,答案包括覆盖REPL的部分以隐藏输入(和输出)以备将来参考,以及覆盖defn以将源存储在元数据中(然后你可以在REPL中轻松检索。