Clojurescript命名空间作为参数

时间:2018-07-24 12:57:49

标签: clojurescript

说我有以下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实现这一目标?

onetwo具有相同的“界面”。

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$3 (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$2 (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)

1 个答案:

答案 0 :(得分:3)

您可以使用ns-resolve函数在命名空间中查找var。

(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。

但是我们可以从命名空间对象手动获取函数。我们还需要使用导出元数据标记来标记函数,以防止函数名称“笨拙”:

(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)