Как я могу показать определение функции в Clojure в REPL?
Я хочу, чтобы REPL печатал текущее определение функции. Есть ли способ сделать это?
Например, данный:
(defn foo [] (if true "true"))
Я хотел бы сказать что-то вроде
(print-definition foo)
и получить что-то вдоль линий
(foo [] (if true "true"))
печатается.
Ответы
Ответ 1
Альтернатива 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 definition in the var's
:source meta-data."
{:arglists (:arglists (meta (var defn)))}
[fn-name & defn-stuff]
`(do (defn ~fn-name [email protected])
(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)
Ответ 2
Вы хотите импортировать пространство имен 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
, который он определяет, источник функции. Затем вы должны написать функцию, которая напоминает этот бит метаданных. Это не должно быть ужасно сложно.
Ответ 3
Clojure не имеет декомпилятора, поэтому это означает, что нет возможности получить источник произвольной функции, если это не было defn, загружаемое с диска. Тем не менее, вы можете использовать аккуратный взлом, называемый serializable-fn, для создания функции, которая имеет исходную форму, хранящуюся в ее метаданных: http://github.com/Seajure/serializable-fn
Ответ на defsource очень похож на этот, но это решение работает с произвольными fns, а не только с верхним уровнем defns. Это также делает fns печатать красиво в repl без специальной функции печати.
Ответ 4
В 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=>
Несколько других функций также автоматически импортируются в пространство имен REPL user
из библиотеки clojure.repl. См. API doc здесь.
Однако, как указано в других ответах здесь, вы не можете использовать source
, а также для печати функций, которые вы определили в REPL.
Ответ 5
Я задал именно этот вопрос в списке рассылки Clojure в последнее время, и ответы включали в себя переопределение частей REPL, чтобы зачеркнуть ввод (и вывод) для будущей ссылки, а также переопределить defn для хранения источника в метаданных (который вы могли бы легко получить в REPL).
Прочитайте поток в списке рассылки Clojure