简体   繁体   English

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

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

I'm looking for the ability to have the REPL print the current definition of a function. 我正在寻找能够让REPL打印出函数的当前定义的能力。 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: source的替代方法(在启动REPL时应该通过clojure.repl/source ,从1.2.0 。如果你使用的是1.1.0或更低版本,则sourceclojure.contrib.repl-utils 。) ,对于REPL使用,而不是查看.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))

A simple print-definition : 一个简单的print-definition

(defn print-definition [v]
  (:source (meta v)))

(print-definition #'foo)

#' is just a reader macro , expanding from #'foo to (var foo) : #'只是一个读者宏 ,从#'foo扩展到(var foo)

(macroexpand '#'reduce)
;; => (var reduce)

You'll want to import the repl namespace, and use the source function from it: 您将要导入repl名称空间,并使用它的source函数:

(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. 虽然这在REPL中不起作用,但只能在类路径上的.clj文件中定义函数。 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. 那么你的问题没有回答:你需要有一个defn ,它在它定义的fn的元数据中存储函数的来源。 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. Clojure没有反编译器,所以这意味着没有办法获得任意函数的源,除非它是从磁盘加载的defn。 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 但是,您可以使用名为serializable-fn的简洁hack来创建一个将源代码存储在其元数据中的函数: 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. defsource答案与此非常相似,但此解决方案适用于任意fns,而不仅仅是顶级定义。 It also makes fns print prettily at the repl without a special printing function. 它还使fns在repl上打印得漂亮,没有特殊的打印功能。

In clojure 1.2's REPL, the source function is immediately available. 在clojure 1.2的REPL中, source函数立即可用。 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. 其他一些函数也会自动从clojure.repl库导入REPL的user命名空间。 See the API doc here . 请在此处查看API文档。

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. 但是,正如在此处的其他答案中所指出的,您不能使用source来打印您在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). 我最近在Clojure邮件列表上确切地问了这个问题,答案包括覆盖REPL的部分以隐藏输入(和输出)以备将来参考,以及覆盖defn以将源存储在元数据中(然后你可以在REPL中轻松检索)。

Read the thread on the Clojure mailing list 阅读Clojure邮件列表中的主题

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM