Clojurescript 命名空间作为参数
Clojurescript namespace as argument
假设我有以下 Clojurescript 代码:
(ns one)
(defn foo [] 1)
(ns two)
(defn foo [] 2)
(ns other)
(defn thing [the-ns] (the-ns/foo))
; now I want to see 1
(other/thing one)
; now I want to see 2
(other/thing two)
如何使用 Clojurescript 实现此目的?
one
和 two
具有相同的“接口”。
PS 我知道我可以将函数作为参数传递,但这并不能回答问题。 (例如命名空间可能有很多功能,我不想全部传递)
尝试过 ns-resolve
boot.user=> (ns one)
nil
one=> (defn foo [] 1)
#'one/foo
one=> (ns two)
nil
two=> (defn foo [] 2)
#'two/foo
two=> (ns other (:require [cljs.analyzer.api :as api]))
nil
other=> (defn thing [the-ns] (let [f (api/ns-resolve the-ns 'foo)] (f)))
#'other/thing
other=> (other/thing 'one)
java.lang.NullPointerException:
other=> (one/foo)
1
other=> (two/foo)
2
(是的,在 java.lang.NullPointerException:
之后没有踪迹,我继续展示在 REPL 会话中解析的初始命名空间,)
如果我离开人为的例子,并在我的 Clojurescript 项目中尝试这个,我得到这个跟踪:
#object[Error Error: No protocol method IDeref.-deref defined for type null: ]
Error: No protocol method IDeref.-deref defined for type null:
at Object.cljs$core$missing_protocol [as missing_protocol] (http://0.0.0.0:8000/index.html.out/cljs/core.js:311:9)
at Object.cljs$core$_deref [as _deref] (http://0.0.0.0:8000/index.html.out/cljs/core.js:2164:17)
at cljs$core$deref (http://0.0.0.0:8000/index.html.out/cljs/core.js:4945:18)
at Function.cljs.analyzer.api.ns_resolve.cljs$core$IFn$_invoke$arity (http://0.0.0.0:8000/index.html.out/cljs/analyzer/api.js:346:51)
at cljs$analyzer$api$ns_resolve (http://0.0.0.0:8000/index.html.out/cljs/analyzer/api.js:322:37)
at Function.cljs.analyzer.api.ns_resolve.cljs$core$IFn$_invoke$arity (http://0.0.0.0:8000/index.html.out/cljs/analyzer/api.js:332:37)
at cljs$analyzer$api$ns_resolve (http://0.0.0.0:8000/index.html.out/cljs/analyzer/api.js:318:37)
at eval (eval at <anonymous> (http://0.0.0.0:8000/index.html.out/weasel/repl.js:30:495), <anonymous>:1:108)
at eval (eval at <anonymous> (http://0.0.0.0:8000/index.html.out/weasel/repl.js:30:495), <anonymous>:9:3)
at eval (eval at <anonymous> (http://0.0.0.0:8000/index.html.out/weasel/repl.js:30:495), <anonymous>:14:4)
您可以使用 ns-resolve 函数在命名空间中查找变量。
(ns one)
(defn foo [] 1)
(ns two)
(defn foo [] 2)
(ns other)
(defn thing [the-ns]
(let [f (ns-resolve the-ns 'foo)]
(f)))
(demo.other/thing 'one) ;; returns 1
(demo.other/thing 'two) ;; returns 2
但对于这种多态行为,使用协议或多方法更合适。
更新
以上代码只能在Clojure中运行,因为ns-resolve
在ClojureScript中不存在。其实ClojureScript是没有Vars的。
但我们可以手动从命名空间对象中获取函数。我们还需要使用导出元数据标志来标记函数,以防止函数名称获取 "munged":
(ns demo.one)
(defn ^:export foo [] 1)
(ns demo.two)
(defn ^:export foo [] 2)
(ns demo.other)
(defn thing [the-ns]
(let [f (aget the-ns "foo")]
(f)))
(other/thing demo.one)
(other/thing demo.two)
假设我有以下 Clojurescript 代码:
(ns one)
(defn foo [] 1)
(ns two)
(defn foo [] 2)
(ns other)
(defn thing [the-ns] (the-ns/foo))
; now I want to see 1
(other/thing one)
; now I want to see 2
(other/thing two)
如何使用 Clojurescript 实现此目的?
one
和 two
具有相同的“接口”。
PS 我知道我可以将函数作为参数传递,但这并不能回答问题。 (例如命名空间可能有很多功能,我不想全部传递)
尝试过 ns-resolve
boot.user=> (ns one)
nil
one=> (defn foo [] 1)
#'one/foo
one=> (ns two)
nil
two=> (defn foo [] 2)
#'two/foo
two=> (ns other (:require [cljs.analyzer.api :as api]))
nil
other=> (defn thing [the-ns] (let [f (api/ns-resolve the-ns 'foo)] (f)))
#'other/thing
other=> (other/thing 'one)
java.lang.NullPointerException:
other=> (one/foo)
1
other=> (two/foo)
2
(是的,在 java.lang.NullPointerException:
之后没有踪迹,我继续展示在 REPL 会话中解析的初始命名空间,)
如果我离开人为的例子,并在我的 Clojurescript 项目中尝试这个,我得到这个跟踪:
#object[Error Error: No protocol method IDeref.-deref defined for type null: ]
Error: No protocol method IDeref.-deref defined for type null:
at Object.cljs$core$missing_protocol [as missing_protocol] (http://0.0.0.0:8000/index.html.out/cljs/core.js:311:9)
at Object.cljs$core$_deref [as _deref] (http://0.0.0.0:8000/index.html.out/cljs/core.js:2164:17)
at cljs$core$deref (http://0.0.0.0:8000/index.html.out/cljs/core.js:4945:18)
at Function.cljs.analyzer.api.ns_resolve.cljs$core$IFn$_invoke$arity (http://0.0.0.0:8000/index.html.out/cljs/analyzer/api.js:346:51)
at cljs$analyzer$api$ns_resolve (http://0.0.0.0:8000/index.html.out/cljs/analyzer/api.js:322:37)
at Function.cljs.analyzer.api.ns_resolve.cljs$core$IFn$_invoke$arity (http://0.0.0.0:8000/index.html.out/cljs/analyzer/api.js:332:37)
at cljs$analyzer$api$ns_resolve (http://0.0.0.0:8000/index.html.out/cljs/analyzer/api.js:318:37)
at eval (eval at <anonymous> (http://0.0.0.0:8000/index.html.out/weasel/repl.js:30:495), <anonymous>:1:108)
at eval (eval at <anonymous> (http://0.0.0.0:8000/index.html.out/weasel/repl.js:30:495), <anonymous>:9:3)
at eval (eval at <anonymous> (http://0.0.0.0:8000/index.html.out/weasel/repl.js:30:495), <anonymous>:14:4)
您可以使用 ns-resolve 函数在命名空间中查找变量。
(ns one)
(defn foo [] 1)
(ns two)
(defn foo [] 2)
(ns other)
(defn thing [the-ns]
(let [f (ns-resolve the-ns 'foo)]
(f)))
(demo.other/thing 'one) ;; returns 1
(demo.other/thing 'two) ;; returns 2
但对于这种多态行为,使用协议或多方法更合适。
更新
以上代码只能在Clojure中运行,因为ns-resolve
在ClojureScript中不存在。其实ClojureScript是没有Vars的。
但我们可以手动从命名空间对象中获取函数。我们还需要使用导出元数据标志来标记函数,以防止函数名称获取 "munged":
(ns demo.one)
(defn ^:export foo [] 1)
(ns demo.two)
(defn ^:export foo [] 2)
(ns demo.other)
(defn thing [the-ns]
(let [f (aget the-ns "foo")]
(f)))
(other/thing demo.one)
(other/thing demo.two)