Eval with local bindings

8 views
Skip to first unread message

Greg Harman

unread,
Jan 17, 2009, 2:06:20 PM1/17/09
to Clojure
Meta: This thread is a revival and continuation of last month's
discussion at:
http://groups.google.com/group/clojure/browse_thread/thread/e1226810b6ac7bfc/8e0f53c141c26fcc?lnk=gst&q=eval+binding#8e0f53c141c26fcc

---

Nathan, did you ever come up with a better way to do this than using a
global var?

One solution is to use (binding), which still requires a global var,
but gives each eval it's own binding of that var:

user=> (def x)
#'user/x
user=> (def expr '(+ x 4))
#'user/expr
user=> (binding [x 3] (eval expr))
7
user=> x
java.lang.IllegalStateException: Var user/x is unbound.
(NO_SOURCE_FILE:0)


Nathan Kitchen

unread,
Jan 17, 2009, 2:15:14 PM1/17/09
to clo...@googlegroups.com

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

Greg Harman

unread,
Jan 17, 2009, 2:27:37 PM1/17/09
to Clojure
Nathan,

Just to confirm two things in eval-expr:

1. Should seq be seq?
2. It looks like you still need to a. have a global x defined to
evaluate eval-expr and b. need to wrap the call to eval-expr with
binding in order to get a non-global binding for x (let doesn't seem
to do the trick).

-Greg

On Jan 17, 2:15 pm, Nathan Kitchen <nathan.kitc...@gmail.com> wrote:
> On Sat, Jan 17, 2009 at 11:06 AM, Greg Harman <ghar...@gmail.com> wrote:
>
> > Meta: This thread is a revival and continuation of last month's
> > discussion at:
> >http://groups.google.com/group/clojure/browse_thread/thread/e1226810b...

Christophe Grand

unread,
Jan 17, 2009, 2:47:52 PM1/17/09
to clo...@googlegroups.com
Greg Harman a écrit :

> One solution is to use (binding), which still requires a global var,
> but gives each eval it's own binding of that var:
>
> user=> (def x)
> #'user/x
> user=> (def expr '(+ x 4))
> #'user/expr
> user=> (binding [x 3] (eval expr))
> 7
> user=> x
> java.lang.IllegalStateException: Var user/x is unbound.
> (NO_SOURCE_FILE:0)
>

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


Christophe Grand

unread,
Jan 18, 2009, 4:54:20 AM1/18/09
to clo...@googlegroups.com
Christophe Grand a écrit :

> You also can do something like this:
>
> (defmacro let-eval [vars expr]
> (let [bindings (mapcat #(list (list `quote %) %) vars)]
> `(eval (list 'let [~@bindings] ~expr))))
>

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

Reply all
Reply to author
Forward
0 new messages