Reputation: 5428
Say I have the following Clojurescript code:
(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)
How can I achieve this with Clojurescript?
one
and two
has the same "interface".
PS I know I can pass a function as an argument, but that doesn't answer the question. (e.g. the namespace might have many functions, and I don't want to pass them all)
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
(Yes, there's no trace after java.lang.NullPointerException:
, and I go on to show the initial namespaces resolve in the REPL session,)
If I move away from the contrived example, and try this in my Clojurescript project, I get this trace:
#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)
Upvotes: 0
Views: 419
Reputation: 2844
You can use ns-resolve function to find a var in a namespace.
(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
But for this kind of polymorphic behavior, using protocols or multi-methods is more suitable.
UPDATE
The above code works only in Clojure, because ns-resolve
does not exists in ClojureScript. In fact, ClojureScript does not have Vars.
But we can manually get the function from namespace object. We also need to mark functions with export metadata flag to prevent function names get "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)
Upvotes: 3