On Thu, Apr 14, 2011 at 10:42 AM, Mark Rathwell <mark.rathw...@gmail.com> wrote:
>
> Actually, that doesn't seem to be the case, seems to have something to do
> with the closure affecting macro expansion, but again, need someone smarter
> to explain:
> user> (defn plus-x [x] (fn [y] (+ x y)))
> #'user/plus-x
> user> (eval (mapper (plus-x 1) [1 2 3]))
> ; Evaluation aborted.
> user> (eval (mapper (fn [x] (+ 1 x)) [1 2 3]))
> (2 3 4)

This is that weird bug/glitch/unsupported feature when an actual
function object (as opposed to a symbol referring to a local or a var
holding one) is eval'd. It seems that this works for non-closure fns
but fails when the fn closes over a value from its context. I assume
that eval of sexps with embedded function objects is not supported,
and that it works even for some function objects is more or less
accidental.

Embed the quoted function code, e.g. (mapper `(partial + ~x) ...)
rather than (mapper (partial + x) ...), or make a defn out of the
function and call it by name, e.g. (defn foo ...) ... (mapper foo
...).

Note that the former only works if x is something eval'able itself,
such as a number or a string or something similar, or a Clojure data
structure composed ultimately of same. If it is, or contains, some
other kind of Java object, you've got trouble again.

The problem with generic objects in eval is partially solvable:

1. If the sexp is constructed and then eval'd in the same session, a
constant reference to the object could simply be embedded in the
generated bytecode as a class constant of reference type.

2. If the code is being AOT compiled, and the object implements
Serializable, it could be serialized as some sort of resource, and the
AOT output could include a static initializer that loads that resource
and stores a reference in a class constant.

Currently, neither of the above is implemented. The case that would
remain if they were would be:

3. The code is being AOT compiled and the object does not implement
Serializable.

That case subdivides as follows:

3a. The object cannot even in principle usefully be preserved across
sessions or moved between JVMs, e.g. because it contains file handles
or other native resources (ex.: FileInputStream).

3b. The object can in principle be preserved, and fairly simply; case
2 above would Just Work(tm) if you just slapped "implements
Serializable" on the thing's class.

3c. The object can in principle be preserved, but not simply; e.g.
WeakReference, which hooks into the GC; you'd need to first preserve
the referent (if not null) and then a) load that and then b) construct
a WeakReference to it in the target JVM instance. ReferenceQueues add
more complication in that instance. 3c could reduce to 3b in the case
of a proper writeObject/readResolve method pair.

However, for Clojure to support case 3b requires some kind of heap
introspection capability (likely involving native code) that allows it
to directly copy the bytes of an object's in-memory representation.
That prevents making 3c work via the writeObject/readResolve method,
and would presumably not work in sandboxed JVMs. I doubt it's
particularly practical to attempt to support case 3, just 1 and 2.

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