Подтвердить что ты не робот

Как я могу показать определение функции в Clojure в REPL?

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

Например, данный:

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

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

(print-definition foo)

и получить что-то вдоль линий

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

печатается.

4b9b3361

Ответ 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