clojureevalclojurescriptedn

How do I evaluate an edn/read list?


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


Solution

  • 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