Интересный вопрос.Я бы никогда не догадался, что ваш код будет выводить user
для первого оператора println.
Проблема в том, что только компилятор Clojure знает имя NS, и это только когда исходный файлскомпилирован.Эта информация теряется до вызова каких-либо функций в NS во время выполнения.Вот почему мы получаем user
из кода: очевидно, что lein вызывает demo.core/-main
из user
ns.
Единственный способ сохранить информацию NS, чтобы она была доступна во время выполнения (против времени компиляции)является принудительное добавление к NS под известным именем, как вы сделали с вашим def
в макросе.Это похоже на трюк Шона (из ссылки Carcingenicate ):
(def ^:private my-ns *ns*) ; need to paste this into *each* ns
Единственный другой подход, о котором я мог подумать, это каким-то образом получить стек вызовов Java, чтобы мы могли выяснить, ктоназывается наша функция "get-ns".Конечно, Java предоставляет простой способ проверки стека вызовов:
(ns demo.core
(:use tupelo.core)
(:require
[clojure.string :as str]))
(defn caller-ns-func []
(let [ex (RuntimeException. "dummy")
st (.getStackTrace ex)
class-names (mapv #(.getClassName %) st)
class-name-this (first class-names)
class-name-caller (first
(drop-while #(= class-name-this %)
class-names))
; class-name-caller is like "tst.demo.core$funky"
[ns-name fn-name] (str/split class-name-caller #"\$")]
(vals->map ns-name fn-name)))
и использования:
(ns tst.demo.core
(:use demo.core tupelo.core tupelo.test)
(:require
[clojure.string :as str]
[demo.core :as core]))
(defn funky [& args]
(spyx (core/caller-ns-func)))
(dotest
(funky))
с результатом:
(core/caller-ns-func) => {:ns-name "tst.demo.core", :fn-name "funky"}
И мы недаже не нужен макрос!