How can I display the definition of a function in Clojure at the REPL?
I'm lo开发者_运维技巧oking for the ability to have the REPL print the current definition of a function. Is there any way to do this?
For example, given:
(defn foo [] (if true "true"))
I'd like to say something like
(print-definition foo)
and get something along the lines of
(foo [] (if true "true"))
printed.
An alternative to source
(which should be available via clojure.repl/source
when starting a REPL, as of 1.2.0
. If you're working with 1.1.0
or lower, source
is in clojure.contrib.repl-utils
.), for REPL use, instead of looking at functions defined in a .clj
file:
(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))
A simple print-definition
:
(defn print-definition [v]
(:source (meta v)))
(print-definition #'foo)
#'
is just a reader macro, expanding from #'foo
to (var foo)
:
(macroexpand '#'reduce)
;; => (var reduce)
You'll want to import the repl
namespace, and use the source
function from it:
(ns myns
(:use [clojure.repl :only (source)]))
(defn foo [] (if true "true"))
(source foo)
=> (foo [] (if true "true"))
nil
Though this wouldn't work in the REPL, only where the function is defined in a .clj file on the classpath. Which doesn't answer your question, then: you'd need to have a defn
that stores, in the metadata of the fn
it defines, the source of the function. Then you'd write a function that recalls that bit of metadata. That shouldn't be terribly difficult.
Clojure doesn't have a decompiler, so that means there's no way to get at the source of an arbitrary function unless it was a defn loaded from disk. However, you can use a neat hack called serializable-fn to create a function that has its source form stored in its metadata: http://github.com/Seajure/serializable-fn
The defsource answer is very similar to this, but this solution works with arbitrary fns, not just top-level defns. It also makes fns print prettily at the repl without a special printing function.
In clojure 1.2's REPL, the source
function is immediately available. You can use it this way:
$ 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=>
A few other functions are also automatically imported into the REPL's user
namespace from the clojure.repl library. See the API doc here.
However, as pointed out in other answers here, you can't use source
as is to print back functions you have defined in the REPL.
I asked exactly this question on the Clojure mailing list recently and the answers included overriding parts of the REPL to stash the input (and output) away for future reference as well as an override of defn to store the source in metadata (which you could then easily retrieve in the REPL).
Read the thread on the Clojure mailing list
精彩评论