On Sat, Mar 12, 2011 at 10:44 AM, Stuart Sierra
<the.stuart.sie...@gmail.com> wrote:
>> 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.
>
> That's easy for simple types like java.util.Date, but how could it work for
> all arbitrary objects?  Those objects may contain pointers to other objects.
>  How would you stuff a java.util.TreeMap into a static field -- perform a
> deep copy of the entire structure?  There's no standard interface for deep
> copy, or even an easily-agreed-upon notion of what deep copy IS.
> Java serialization runs into the same problems.  Where do you stop?  You
> have objects with volatile fields or pointers to system resources like
> filehandles.  How would you stuff a java.io.InputStream into a static field?
>  That's not to say you couldn't write Clojure functions to embed serialized
> data in
>>
>> private static final Date blah = somedate;
>
> You can't do that, even in Java.  The constant pool in a compiled class can
> only contain primitives, Strings, and names.  Where does `somedate` come
> from?  You have to provide code to construct a java.util.Date, which javac
> will add to the static initializer block of the class.  Clojure does
> essentially the same thing with `print-dup`.
> The deeper issue here is that JVM bytecode is fundamentally static: it's not
> allowed to contain references to objects that only exist at runtime.  In
> languages that have a built-in concept of a "runtime image," like Smalltalk
> or some Lisps, the line between static code and runtime data is blurred.

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 ...))

!!

-- 
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clojure@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+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en

Reply via email to