0
(def a (edn/read-string "(+ 1 3)"))
; => (+ 1 3)

How do I evaluate this resulting list?

(type (first a))
; => cljs.core/Symbol

(= (first a) '+)
; => true

I suppose more generally how would I get from symbol -> function. And is this a normal practice in clojure? I can't seem to find anything on it. Perhaps I'm not searching with the correct terms.

2 Answers2

5

You'd normally use eval. But in ClojureScript, you'd need the compiler and standard lib available at runtime. This is only possible if you are making use of self-hosted ClojureScript.

If you are in a self-hosted environment (such as Lumo, Planck, Replete, Klipse, etc.), then eval will just work:

cljs.user=> (require '[clojure.edn :as edn])
nil
cljs.user=> (def a (edn/read-string "(+ 1 3)"))
#'cljs.user/a
cljs.user=> (eval a)
4

Otherwise, you can make use of the facilities in the cljs.js namespace to access self-hosted ClojureScript:

cljs.user=> (require 'cljs.js)
nil
cljs.user=> (cljs.js/eval (cljs.js/empty-state)
  a {:eval cljs.js/js-eval :context :expr} prn)
{:value 4}

Note that doing this carries some size considerations: The ClojureScript compiler will be brought with your compiled artifacts into the target environment, and you must also avoid using :advanced, ensuring that the entire cljs.core standard lib and associated metadata is available at runtime.

Mike Fikes
  • 3,507
  • 14
  • 28
2

My answer seems to only work in Clojure, not ClojureScript. See the other answer.


I think you may be looking for resolve.

(defn my-simple-eval [expr]
  ; Cut the function symbol from the arguments
  (let [[f & args] (edn/read-string expr)]
    ; Resolve f to a function then apply the supplied arguments to it 
    (apply (resolve f) args)))

(my-simple-eval "(+ 1 3)")
=> 4

The arguments must be bare numbers for this to work though. If you want to allow for sub-expressions, you could make it recursive:

(defn my-simple-eval-rec [expr]
  (letfn [(rec [[f & args]]
            (->> args
                 (map (fn [arg]
                        (if (list? arg)
                          (rec arg) ; Process the sub-expr
                          arg)))

                 (apply (resolve f))))]

    (rec (edn/read-string expr))))

(my-simple-eval-rec "(+ 1 (+ 2 5))")
=> 8

If this doesn't suffice though, I don't know of any way other than using eval:

(def a (edn/read-string "(+ 1 3)"))

(eval a)
=> 4

or, if the data is available at the time macros are expanded, you could just wrap the call to read-string to have the data interpreted as normal:

(defmacro my-read-string [expr]
  (edn/read-string expr))

(my-read-string "(+ 1 3)")
=> 4
Carcigenicate
  • 43,494
  • 9
  • 68
  • 117
  • Ahh ok, thanks for the answers. So it looks like I've been working in a cljs repl. These work in a normal repl. This is what I get when using resolve on the symbol in cljs: clojure.lang.ExceptionInfo : java.lang.AssertionError : Assert failed: Argument to resolve must be a quoted symbol (core/and (seq? quoted-sym) (= (quote quote) (first quoted-sym))) – koboldsinparis May 05 '19 at 03:18
  • @koboldsinparis Honestly, I don't know ClojureScript. I can try to look into it in the morning. – Carcigenicate May 05 '19 at 03:52