"Can't eval locals problem"

489 views
Skip to first unread message

Razvan Rotaru

unread,
Oct 3, 2011, 12:54:07 PM10/3/11
to Clojure
Ok, so I'm stuck. If any of you more seasoned clojurians have a hint
that could get me out, I will be forever gratefull to him/her:

I'm trying execute a query against google app engine datastore, using
appengine-magic, with the filter dynamically generated from a map.
Here's the closest code I have been able to come up with:

(defmacro order-query [params]
`(ds/query :kind Order :filter ~(map #(list (key %) (val %)) (eval
params))))

This macro fails with a "Can't eval locals" error. I recognize that
the main issue here is that i'm forcing evaluation of something at
macro expansion time. Apparently clojure does not (fully) support
this, so I'm looking then for alternatives.

Cheers,
Razvan

Stuart Halloway

unread,
Oct 3, 2011, 1:40:00 PM10/3/11
to clo...@googlegroups.com

Hi Razvan,

Can you tell us a bit more:

(1) the stack trace of the error you are seeing

(2) the form you are using to call order-query

(3) what you expect evaling something at macroexpansion time should be doing

Thanks,
Stu


Joop Kiefte

unread,
Oct 3, 2011, 1:39:50 PM10/3/11
to clo...@googlegroups.com
Try (defmacro order-query [params]
 `(ds/query :kind Order :filter (map #(list (key %) (val %)) ~params))) or 
(defmacro order-query [params]
 `(ds/query :kind Order :filter (map #(list (key %) (val %)) (eval ~params)))) depending on the necessity of the eval (I suppose you don't really need the eval... the problem was using the eval in the evaluation of a macro I guess... ;)).

2011/10/3 Razvan Rotaru <razvan...@gmail.com>

--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clo...@googlegroups.com
Note that posts from new members are moderated - please be patient with your first post.
To unsubscribe from this group, send email to
clojure+u...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en

Michał Marczyk

unread,
Oct 3, 2011, 4:25:50 PM10/3/11
to clo...@googlegroups.com
eval does work at macro expansion time, but only where it is actually
possible to evaluate the expression its given with only the
information available at macro expansion time at hand.

One case where there is not enough information -- and a minimal "Can't
eval locals" example -- is the following:

(defmacro foo [x] (eval x))

(let [y 1] (foo y))

Are you doing something similar...?

eval's behaviour here is very sensible, since it is asked to eval the
Symbol y at compile time, when it knows that it names a local binding,
but does not know what the value of that local binding is going to be
at runtime. (It could conceivably notice that this particular local is
given a constant initializer expression, but that would only raise
expectations for cases like (let [y (some-function)] (foo y)) where
using the compile-time return value of (some-function) would make no
sense even if it were possible to obtain it.)

That doesn't mean eval'ing stuff at compile time is not possible, it's
just that one has to make sure that one is eval'ing forms which have
meaningful compile-time values.

Sincerely,
Michał


PS. Actually it's fun to go code diving to see how this breaks
exactly... Although it's not terribly helpful in writing code. A
summary of the code path this goes through is, I think, that
clojure.core/eval delegates to clojure.lang.Compiler.eval, which in
turn needs the expression analysed by clojure.lang.Compiler.analyze,
which in this case delegates to clojure.lang.Compiler.analyzeSymbol,
which in this case -- called at compile-time inside this particular
let form -- finds a binding for the Symbol y included in the local
environment and accordingly returns a LocalBindingExpr, which is
something un-eval-able.

Michał Marczyk

unread,
Oct 3, 2011, 4:49:29 PM10/3/11
to clo...@googlegroups.com
Ok, so I mailed off the above discussion without including any
suggestions for alternative approaches... *fail*

Anyway, I think Joop's answer (especially the first code snippet) is
*very* likely to be the best. As far as I can tell from reading the
source, appengine-magic's query macro includes the filter expression
in its expansion verbatim, so it will just be evaluated at run time.

If you do want to play around with some super-flexible eval-based
approaches, you might want to give Michael Fogus's highly enjoyable
evalive library [1] a try -- it provides an aptly named #'evil
function which, together with its companion macro #'local-bindings (or
was it #'local-env...), does allow one more flexibility in what one
evals. The usual warnings (don't eval unless you have to etc.) apply.

Sincerely,
Michał


[1] https://github.com/fogus/evalive

Razvan Rotaru

unread,
Oct 19, 2011, 3:10:33 PM10/19/11
to Clojure
Thanks for your suggestion. I finally decided to find a way around the
problem. :) This is cutting too deep for me to handle with my current
clojure knowledge.
I also talked to Constantine Vetoshev (the author of appengine-magic)
and he anknowledged it as an issue which could have a solution by
changing the query macro. So, for the moment I'm waiting for a new
version.

Thanks anyway. I really appreciate the activity on this group.

Razvan
Reply all
Reply to author
Forward
0 new messages