5

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

szymanowski
  • 1,359
  • 1
  • 14
  • 25

4 Answers4

5

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
Marcin Bilski
  • 572
  • 6
  • 13
4

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.

Ryan
  • 496
  • 6
  • 13
1

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

yazz.com
  • 57,320
  • 66
  • 234
  • 385
1

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.

Terje Norderhaug
  • 3,649
  • 22
  • 25