Вы можете использовать макрос clojure.repl/source
, чтобы получить источник символа:
user> (source max)
(defn max
"Returns the greatest of the nums."
{:added "1.0"
:inline-arities >1?
:inline (nary-inline 'max)}
([x] x)
([x y] (. clojure.lang.Numbers (max x y)))
([x y & more]
(reduce1 max (max x y) more)))
nil
Но это только часть ответа.AFAICT source
ищет имя файла источника и номер строки, которые определяют данный символ, а затем печатает исходный код из файла.Следовательно, source
не будет работать с символами, для которых у вас нет исходного кода, например, скомпилированный AOT код clojure.
Возвращаясь к исходному вопросу, вы можете думать о source
как о чтении мета.данные, связанные с данным символом и просто печать этого.Т.е. это обман.Это никоим образом не возвращает вам «код как данные», где под кодом я подразумеваю скомпилированную функцию clojure.
На мой взгляд, «код как данные» относится к функции lisps, где исходный код фактически являетсяСтруктура данных LISP, и, следовательно, она может быть прочитана читателем LISP.То есть я могу создать структуру данных, которая является допустимым кодом LISP, и eval
, что.
Например:
user=> (eval '(+ 1 1))
2
Здесь '(+ 1 1)
- это буквальный список, который читаетсясчитывателем clojure, а затем оцененным как код clojure.
Обновление: В одном из комментариев Йеонатан Шарвит спрашивал, можно ли изменить код для функции.Следующий фрагмент читает исходный код функции, изменяет результирующую структуру данных и, наконец, оценивает структуру данных, в результате чего определяется новая функция my-nth
:
(eval
(let [src (read-string (str (source-fn 'clojure.core/nth) "\n"))]
`(~(first src) my-nth ~@(nnext src))))
Строка syntax-quote
заменяет nth
на my-nth
в форме defn
.