代码之家  ›  专栏  ›  技术社区  ›  opyate

ClojureScript命名空间作为参数

  •  0
  • opyate  · 技术社区  · 6 年前

    假设我有以下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 具有相同的“接口”。

    我知道我可以传递一个函数作为参数,但这不能回答问题。(例如,名称空间可能有许多函数,我不想全部传递它们)

    尝试 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 回复  |  直到 6 年前
        1
  •  3
  •   opyate    6 年前

    你可以使用 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。

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

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