Как увидеть локальные переменные Clojure в локальном repl?

Я хочу поиграть и разработать выражения, основанные на локальных переменных, поместив repl (с clojure.main / repl) внутри тела функции:

(ns something)

(defn myfunc [ p ]
   (let [local (+ p 10)]
        (clojure.main/repl)
        (+ local 100)))

(myfunc 666)

Когда я выполнил это, repl запускается нормально, но параметры функции и локальные привязки let не отображаются в приглашении:

something=> p
CompilerException java.lang.RuntimeException: Unable to resolve symbol: p in this context
something=> local
CompilerException java.lang.RuntimeException: Unable to resolve symbol: local in this context

Я смог передать значения, создав новые переменные ^:dynamic и установив их значения локально с привязкой, но это довольно сложно и требует отдельной привязки для каждой локальной переменной:

(def ^:dynamic x)

(defn myfunc [ p ]
   (let [local (+ p 10)]
        (binding [x local]
                 (clojure.main/repl))
        (+ local 100)))

Есть ли более простой способ передать / получить доступ к локальным значениям в такой локальной реплике? Или есть какой-то лучший способ получить доступ к локальным переменным из нелокального repl, например, "lein repl"?

2 ответа

Решение

С использованием :init хук, вы можете определить произвольные переменные в пространстве имен REPL.

(defn myfunc [p]
  (let [local (+ p 10)]
    (clojure.main/repl :init #(do (def p p) (def local local)))
    (+ local 100)))

Вот repl макрос для облегчения добавления точки останова:

(defmacro locals []
  (into {}
        (map (juxt name identity))
        (keys &env)))

(defn defs [vars]
  (doseq [[k v] vars]
    (eval (list 'def (symbol k) (list 'quote v)))))

(defmacro repl []
  `(let [ls# (locals)]
     (clojure.main/repl :init #(defs ls#))))

Теперь вы можете просто зайти (repl):

(defn myfunc [p]
  (let [local (+ p 10)]
    (repl)
    (+ local 100)))

Я не знаю хорошего ответа для использования repl, но я предпочитаю использовать старые добрые распечатки. Этому способствует spyx, let-spy, а также let-spy-pretty макросы:

(ns tst.demo.core
  (:use demo.core tupelo.core tupelo.test))

(defn myfunc [ p ]
  (spyx p)
  (let-spy [local (+ p 10)]
    (+ local 100)))

(dotest
  (spyx (myfunc 666)))

с результатом:

p              => 666
local          => 676
(myfunc 666)   => 776

Документация по spyx & friends находится здесь в README, а также есть полная документация по API на страницах GitHub.

Другие вопросы по тегам