How can I evaluate local symbols using eval function in Clojure?

400 views
Skip to first unread message

Philos Kim

unread,
Sep 6, 2016, 5:41:49 AM9/6/16
to Clojure
I need to evaluate local symbols using eval function in Clojure.

(def a 1)
(def b 2)

(let [a 10
      b 20]
  (eval '(+ a b)))
;=> 3

I expected the result to be 30, but the result is 3. I want to know why Clojure eval function doesn't evaluate the local symbols.

And any alternative to get around?

William Parker

unread,
Sep 6, 2016, 6:35:55 AM9/6/16
to Clojure
Eval expects to get symbols that are resolvable in the current namespace, so that won't work (as you mention).  However, in this case it is pretty easy to work around.  You just need to embed the values of a and b into the code that is evaluated, and since they are primitives this should work.  I'd do this like `(+ ~a ~b).  The ` is called "syntax-quote" (you can google for that, not that you shouldn't ask questions if something is unclear :) ).  Basically you'll get code like (clojure.core/+ 10 20) that way; the tilde "~" puts the local values of a and b in the code block rather than the symbols a and b.
Message has been deleted

Philos Kim

unread,
Sep 6, 2016, 7:30:09 AM9/6/16
to Clojure
I appreciate your reply.

The following is the entire code where I encountered this problem.
My intention was for debugging purpose to print the result of every nested expression in a form.


(ns debux.lab
  (:require (clojure [walk :as walk])))

;; For debugging
(defmacro dbg_
  [form]
  `(let [return# ~form]
     (println ">> dbg_:" (pr-str '~form) "=>" return# "<<")
     return#))

(def a 2)
(def b 3)
(def c 5)

(defn- dispatch
  [node]
  (cond
    (list? node)
    (do (eval `(dbg_ ~node))
        node)

    (and (symbol? node)
         (not (fn? (eval `~node))))
    (do (eval `(dbg_ ~node))
        node)

    :else node))

(defn- tree-walk
  [tree]
  (walk/postwalk dispatch tree))


;; dbg for nested expressions
(defmacro dbgn [form]
  (tree-walk form))


;;; test samples

;; This works because every symbol is declared in global symbols
(dbgn (* c (+ a b)))
; >> dbg_: c => 5 <<
; >> dbg_: a => 2 <<
; >> dbg_: b => 3 <<
; >> dbg_: (+ a b) => 5 <<
; >> dbg_: (* c (+ a b)) => 25 <<


;; This works too, because literal syntax-quotes are used.
(let [a 10 b 20 c 30]
  (eval `(* ~c (+ ~a ~b))))
; => 900


;; But this doesn't work, because literal syntax-quotes can't be used in this case.
(let [a 10 b 20 c 30]
  (dbgn (* c (+ a b))))
;   2. Unhandled clojure.lang.Compiler$CompilerException
;      Error compiling work/philos/debux/src/debux/lab.clj at (52:3)
;   
;   1. Caused by java.lang.UnsupportedOperationException
;      Can't eval locals
;   
;                Compiler.java: 5943  clojure.lang.Compiler$LocalBindingExpr/eval
;                Compiler.java: 6932  clojure.lang.Compiler/eval
;                Compiler.java: 6890  clojure.lang.Compiler/eval
;                     core.clj: 3105  clojure.core/eval
;                     core.clj: 3101  clojure.core/eval
;                ......


Any suggestion in this case?

Beau Fabry

unread,
Sep 6, 2016, 1:18:17 PM9/6/16
to Clojure
Hi Philos,

Without getting into how to implement this, the `spyscope` library and the `clojure.tools.logging` library implement this pattern in their `spy` macro and #spy/d reader literal respectively. So you could either add them to your development toolkit (recommended) or you could read their implementations.


Cheers,
Beau
Reply all
Reply to author
Forward
0 new messages