I tried this approach with (binding) and I also tried creating
anonymous functions from the expressions:
(eval (list 'fn '[x] expr))
Creating functions didn't work for me because I used up the PermGen
space in the garbage collector with all the
classes created to implement them. As best I remember, I got the same
outcome with (binding), even though I wasn't creating new functions.
The approach that worked for me was to create my own recursive
evaluation function:
(defn eval-expr [expr]
(cond
(seq expr)
(let [[op & args] expr]
(apply @(resolve op) (map eval-expr args)))
(= expr 'x) x
:else expr))
I'm still using the var x, but it's not inherent to the approach; you
could easily add a map of symbols to values as an additional argument.
-- Nathan
You also can do something like this:
(defmacro let-eval [vars expr]
(let [bindings (mapcat #(list (list `quote %) %) vars)]
`(eval (list 'let [~@bindings] ~expr))))
(let [x 12]
(let-eval [x] '(+ x 2))); returns 14
Christophe
Hmm... not quite:
user=> (let [x [2 3]] (let-eval [x] '(rest x)))
(3)
user=> (let [x '(2 3)] (let-eval [x] '(rest x)))
java.lang.ClassCastException: java.lang.Integer cannot be cast to
clojure.lang.IFn (NO_SOURCE_FILE:0)
The following code fixes this bug:
(defmacro let-eval [vars expr]
(let [bindings (mapcat #(list (list `quote %) (list `list ``quote %))
vars)]
`(eval (list 'let [~@bindings] ~expr))))
user=> (let [x '(2 3)] (let-eval [x] '(rest x)))
(3)
user=> (let [x 12] (let-eval [x] '(+ x 15)))
27
But maybe this is better:
(defmacro let-eval [bindings expr]
(let [binding-forms (map #(list `quote %) (take-nth 2 bindings))
expr-forms (map #(list `list ``quote %) (take-nth 2 (rest
bindings)))]
`(eval (list 'let [~@(interleave binding-forms expr-forms)] ~expr))))
user=> (let-eval [x 12] '(+ x 15))
27
user=> (let-eval [x '(2 3)] '(rest x))
(3)
user=> (let-eval [[x y] [2 3]] '(+ x y))
5
Christophe