I'm searching for a way to call a function given its string name in clojureScript.
Something like:
(call "my-fun" args)
Any help welcome
I'm searching for a way to call a function given its string name in clojureScript.
Something like:
(call "my-fun" args)
Any help welcome
A pretty hackish solution that should work:
(ns eval.core
(:require [clojure.string :as str]))
(defn ->js [var-name]
(-> var-name
(str/replace #"/" ".")
(str/replace #"-" "_")))
(defn invoke [function-name & args]
(let [fun (js/eval (->js function-name))]
(apply fun args)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Usage:
(ns example.core
(:require [eval.core :as e]))
(defn ^:export my-fn [arg1 arg2] ;; Note the export!
(println arg1 arg2)
arg2)
(e/invoke "example.core/my-fn" 5 6) ;=> 5 6
I needed a way of calling a function whose ns/name is loaded dynamically. As noted, there is no supported way of doing so. However, that's what hacks are for. Here's what I ended up doing:
Calling cljs:
(js/proxy "my-ns/my-fun" args)
Proxy.js:
function proxy() {
var args = Array.prototype.slice.call(arguments);
var nsFunc = args[0].replace(/-/g, "_").replace(/\//g, ".");
eval(nsFunc).apply(null, args.slice(1));
}
Dynamically resolved cljs:
(ns my-ns)
(defn ^:export my-fun [args] ...)
The export metadata tells the closure compiler not to munge the name, so this works even with advanced mode compilation. Needless to say, this isn't rock-solid code that's guaranteed to work in the future - but you get the idea.
This is not possible, as once you use the advanced mode compilation of clojurescript then all function names are "munged", so there would be no way to map a string to a munged function when you wanted to call it
As long as you don't use advanced optimization for the compiler:
(def global (this-as this this))
(defn call [fname & args]
(.apply (aget global fname) global args))
(call "String" 123)
global
is bound to the global object for any platform by using the this
operator in the global scope. You may use .call
instead of .apply
as in:
(.call (aget global "String") global 123)
The same technique can also be used with other objects than the global.