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

Tim*_*her 33 clojure

我正在寻找让REPL打印出函数当前定义的能力.有没有办法做到这一点?

例如,给定:

(defn foo [] (if true "true"))
Run Code Online (Sandbox Code Playgroud)

我想说点什么

(print-definition foo)
Run Code Online (Sandbox Code Playgroud)

并得到一些东西

(foo [] (if true "true"))
Run Code Online (Sandbox Code Playgroud)

打印.

May*_*iel 20

替代source(应该clojure.repl/source在启动REPL时可用,1.2.0如果你正在使用1.1.0或更低,source是在clojure.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))
Run Code Online (Sandbox Code Playgroud)

一个简单的print-definition:

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

(print-definition #'foo)
Run Code Online (Sandbox Code Playgroud)

#'只是一个读者宏,从:扩展#'foo(var foo):

(macroexpand '#'reduce)
;; => (var reduce)
Run Code Online (Sandbox Code Playgroud)


Isa*_*aac 18

您将要导入repl命名空间,并使用source它中的函数:

(ns myns
    (:use [clojure.repl :only (source)]))
(defn foo [] (if true "true"))
(source foo)

=> (foo [] (if true "true"))
    nil
Run Code Online (Sandbox Code Playgroud)

虽然这在REPL中不起作用,但只能在类路径上的.clj文件中定义函数.然而,你没有回答你的问题:你需要defnfn它定义的元数据中存储函数的来源.然后你会编写一个函数来回忆那些元数据.这应该不是非常困难.


小智 13

Clojure没有反编译器,所以这意味着没有办法获得任意函数的源,除非它是从磁盘加载的defn.但是,您可以使用名为serializable-fn的简洁hack来创建一个将源代码存储在其元数据中的函数:http://github.com/Seajure/serializable-fn

defsource答案与此非常相似,但此解决方案适用于任意fns,而不仅仅是顶级定义.它还使fns在repl上打印得漂亮,没有特殊的打印功能.


Art*_*ein 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=>

其他一些函数也会自动user从clojure.repl库导入到REPL的命名空间中.请在此处查看API文档.

但是,正如在此处的其他答案中所指出的那样,您不能source按原样使用打印在REPL中定义的函数.


Sea*_*eld 5

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

阅读 Clojure 邮件列表上的主题