How can I display the definition of a function in Clojure at the REPL?

强颜欢笑 提交于 2019-12-28 03:46:06

问题


I'm looking 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.


回答1:


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)



回答2:


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.




回答3:


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.




回答4:


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.




回答5:


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



来源:https://stackoverflow.com/questions/3782970/how-can-i-display-the-definition-of-a-function-in-clojure-at-the-repl

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!