Как я могу отобразить определение функции в Clojure в REPL? - PullRequest
33 голосов
/ 24 сентября 2010

Я ищу возможность, чтобы REPL печатал текущее определение функции. Есть ли способ сделать это?

Например, дано:

(defn foo [] (if true "true"))

Я бы хотел сказать что-то вроде

(print-definition foo)

и получите что-то вроде

(foo [] (if true "true"))

распечатаны.

Ответы [ 5 ]

20 голосов
/ 24 сентября 2010

Альтернатива 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))

Простой print-definition:

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

(print-definition #'foo)

#' - это просто макрос для чтения , расширяющийся с #'foo до (var foo):

(macroexpand '#'reduce)
;; => (var reduce)
18 голосов
/ 24 сентября 2010

Вы хотите импортировать пространство имен 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, которые он определяет, источник функции. Затем вы бы написали функцию, которая вызывает этот бит метаданных. Это не должно быть ужасно сложно.

13 голосов
/ 02 октября 2010

Clojure не имеет декомпилятора, так что это означает, что нет никакого способа получить источник произвольной функции, если это не был defn, загруженный с диска. Однако вы можете использовать аккуратный хак с именем serializable-fn, чтобы создать функцию, исходная форма которой хранится в ее метаданных: http://github.com/Seajure/serializable-fn

Ответ defsource очень похож на этот, но это решение работает с произвольными fns, а не только с высокоуровневыми defns. Это также делает fns печатать красиво в repl без специальной функции печати. ​​

11 голосов
/ 24 сентября 2010

В REPL в clojure 1.2 функция source доступна сразу.Вы можете использовать его следующим образом:

$ java -cp clojure.jar clojure.main
Clojure 1.2.0
user=> <b>(source slurp)</b>
(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=>

Несколько других функций также автоматически импортируются в пространство имен REPL user из библиотеки clojure.repl.См. API doc здесь .

Однако, как указано в других ответах, вы не можете использовать source, как есть, для печати функций, определенных вами в REPL.

4 голосов
/ 24 сентября 2010

Недавно я задал именно этот вопрос в списке рассылки Clojure, и ответы включали переопределение частей REPL для скрытия ввода (и вывода) для дальнейшего использования, а также переопределение defn для сохранения источника в метаданных (который Вы можете легко получить в REPL).

Читать ветку в списке рассылки Clojure

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...