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

瀏覽次數:406 次
跳到第一則未讀訊息

Philos Kim

未讀,
2016年9月6日 清晨5:41:492016/9/6
收件者: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

未讀,
2016年9月6日 清晨6:35:552016/9/6
收件者: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.
訊息已遭刪除

Philos Kim

未讀,
2016年9月6日 清晨7:30:092016/9/6
收件者: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

未讀,
2016年9月6日 下午1:18:172016/9/6
收件者: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
回覆所有人
回覆作者
轉寄
0 則新訊息