A more interesting question is "why does this error exist in the first
place"? Why doesn't eval (or macro expansion) accept general objects
in the input rather than just a few specific types (strings, numbers,
keywords, lists, sets, maps, vectors, etc.)? There are use cases where
it would be quite convenient if all objects that presently produced
that error were instead self-evaluating in the manner of numbers,
strings, and keywords. For one thing, as an optimization you could
precompute some expensive-to-construct object and directly embed it in
compiled code instead of having to calculate it at runtime, or muck
about with computing it, saving it out to a file, reading it on
startup, and adding all the file and directory configuration options
that start to be needed as soon as your app has external data files it
depends on.
Possibly-related (and sometimes equally inconvenient) is that actual
compiled Clojure function objects do *not* produce that error, and
work if they are not closures, but produce cryptic errors when
subsequently invoked if they *are* closures, as though they got copied
without the closed-over values or something instead of just embedded
directly.
Here is the weird behavior of function objects in eval (same result in
macro expansions, when the macro-generated code is actually run):
user=> (def f (let [x 10] (fn [] x))) ; f is a closure
#'user/f
user=> (f) ; works when directly called at repl
10
user=> (eval '((let [x 10] (fn [] x)))) ; create the closure and call
it inside eval -- works
10
user=> (eval f) ; eval the closure itself -- returns the function object
#<user$fn__1429$fn__1430 user$fn__1429$fn__1430@123b9c1>
user=> (eval '(f)) ; eval can call the function through its var, and it works
10
user=> (eval `(~f)) ; eval calling the closure directly embedded in
the call expression -- weird exception
#<CompilerException java.lang.ExceptionInInitializerError (NO_SOURCE_FILE:40)>
user=> (def f (fn [] 10)) ; replace f with a non-closure function with
the same return value
#'user/f
user=> (eval `(~f)) ; eval calling the function directly embedded in
the call expression -- works!
10
user=>
Yes, but "create a static final member in the class I'm generating
bytecode for, stuff the object in that static member, and embed a
reference to the static member here" seems like a sensible thing for
it to do.
private static final Date blah = somedate;
...
something.somemethod(blah);
works in javac, but clojure's compiler hates
(eval '(.somemethod something ~somedate))
for whatever reason.
> The #= is a Clojure reader macro that means "evaluate the following code."
> The error message "maybe print-dup not defined" is suggesting that, if you
> really did mean to stick a java.util.Date in your compiled code, you have to
> provide your own implementation of print-dup. Specifically, you need a
> method of print-dup that takes a java.util.Date and returns a data structure
> representing the Clojure code that *creates* a java.util.Date.
So, there is a workaround of sorts. A general one for serializable
objects could even be constructed: serialize to a byte array, embed
the byte array as a seq in code that turns it back into a byte array
and then deserializes an object from it. Not very efficient though.
A true fix requires changing the compiler to add static final members
to the generated class, as suggested above.
Yes, but "create a static final member in the class I'm generating
bytecode for, stuff the object in that static member, and embed a
reference to the static member here" seems like a sensible thing for
it to do.
private static final Date blah = somedate;
The objections you raise might be applicable when AOT compiling. They
definitely aren't otherwise, because the object that is needed
*already exists* in the same runtime instance where it will be needed.
The compiler can just create a class with a static reference pointing
at the preexisting object in that case.
The AOT compilation case is, meanwhile, the easier one to work around:
(let [x (some-complicated-object)]
(defmacro foo [args ... & body]
`(blah
(blah blah ~some-arg
(blah ~x blah blah
~@body)))))
can be rewritten as
(def x-some-suffix-that-ensures-no-collisions (some-complicated-object))
(defmacro foo [args ... & body]
`(blah
(blah blah ~some-arg
(blah x-some-suffix-that-ensures-no-collisions blah blah
~@body))))
The main case where it can be a problem is with eval rather than
macros, because whereas we can extract an extra def with macros, like
above, how would you do something like this?
(eval `(fn [x#]
(some-complex-code x#
{:time-of-evaluation ~(Date.)}
more-stuff)))
Obviously changing ~(Date.) to just (Date.) is wrong in this case,
since it's supposed to be :time-of-evaluation, not
:time-of-function-call; if ~(Date.) worked as intended the newborn
function would embed the same date in the map (the date of the
function's creation) every time it was called, whereas using (Date.)
results in the date being different every time the function is called.
At the same time we can hardly go around (re)defing the Date at
runtime. Whatever contains the eval would not be reentrant and we
wouldn't be able to have two functions floating around that it created
that have different dates. In this instance we can transform it into a
closure:
(eval `(let [d# (Date.)]
(fn [x#]
(some-complex-code x#
{:time-of-evaluation d#}
more-stuff))))
but there are other cases where it's much less trivial a
transformation. For example, the object may already exist before eval
is called and it may be something that can't be easily replaced or
recreated without repercussions, e.g. a database connection. Right
now, the *simplest* way I can think of to generate a new function
(that isn't simply a closure) at runtime that holds a preexisting
non-global database connection is
(let [foo (gensym)
ns *ns*]
(eval `(ns ~foo))
(in-ns ns)
(let [n (find-ns foo)
v (clojure.lang.Var/intern n 'db the-db-connection-we-already-have)
db-sym (symbol (str foo) "db")]
(eval `(fn [x#] ... ~db-sym ...)))
which is not only insanely ugly but in a sufficiently long-running app
will eventually blow the heap since it will create an open ended set
of Var and Namespace objects that never get garbage collected.
Of course, it's not that common to be needing eval at run-time, but
it's not unheard of either; generating compiled functions on the fly
is sometimes desired as an optimization or for distributed-computing
type purposes. And then it could be handy to allow generic objects to
be self-evaluating in non-AOT-compiled code, as it would reduce the
above horror to:
(eval `(fn [x#] ... ~the-db-connection-we-already-have ...))
!!