我想要一个宏this-ns,以便它返回被调用位置的名称空间。例如,如果我有这个代码
(ns nstest.main
(:require [nstest.core :as nstest]))
(defn ns-str [x]
(-> x (.getName) name))
(defn -main [& args]
(println "The ns according to *ns*:" (ns-str *ns*))
(println "The actual ns:" (ns-str (nstest/this-ns))))
Run Code Online (Sandbox Code Playgroud)
我希望调用lein run会产生这个输出:
The ns according to *ns*: user
The actual ns: nstest.main
Run Code Online (Sandbox Code Playgroud)
我想出的实现是以下代码:
(ns nstest.core)
(defmacro this-ns []
(let [s (gensym)]
`(do (def ~s)
(-> (var ~s)
(.ns)))))
Run Code Online (Sandbox Code Playgroud)
它似乎确实有效,但感觉非常hacky。值得注意的是,在上面的例子中,它会扩展到在函数内部def调用,感觉不是很干净。-main
我的问题:有没有更好的方法来实现this-ns获取this-ns被调用的命名空间?
这是另一种变体:
(defmacro this-ns []
`(->> (fn []) str (re-find #"^.*?(?=\$|$)") symbol find-ns))
Run Code Online (Sandbox Code Playgroud)
事情是匿名函数被编译成一个名为类似的类
playground.core$_main$fn__181@27a0a5a2,所以它以函数被编译的实际命名空间的名称开头。
不能说它看起来不那么笨拙,那么您的变体,它仍然避免了def在您的情况下引入的副作用。
有趣的问题。我永远不会想到你的代码会输出user第一个 println 语句。
问题是只有 Clojure 编译器知道 NS 的名称,而且只有在编译源文件时才知道。在运行时调用 NS 中的任何函数之前,此信息就会丢失。这就是为什么我们user从代码中得到:显然是demo.core/-main来自userns 的 lein 调用。
保存 NS 信息以便在运行时(相对于编译时)可访问的唯一方法是强制以已知名称添加到 NS,就像您def在宏中所做的那样。这类似于 Sean 的技巧(来自Carcingenicate 的链接):
(def ^:private my-ns *ns*) ; need to paste this into *each* ns
Run Code Online (Sandbox Code Playgroud)
我能想到的唯一其他方法是以某种方式获取 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)))
Run Code Online (Sandbox Code Playgroud)
和用法:
(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))
Run Code Online (Sandbox Code Playgroud)
结果:
(core/caller-ns-func) => {:ns-name "tst.demo.core", :fn-name "funky"}
Run Code Online (Sandbox Code Playgroud)
我们甚至不需要宏!
| 归档时间: |
|
| 查看次数: |
97 次 |
| 最近记录: |