On Wed, May 4, 2011 at 7:03 PM, Simon Katz <nomisk...@gmail.com> wrote:
> It is not possible to write out a function object (something returned
> by lambda, Common Lisp's equivalent of fn) to a compiled file.  No mix
> of eval-when, separating functions and macros, and load order can
> change that.

The same is not true of Clojure functions, though. Any function object
reference can be turned in bytecode into a constructor call to the
appropriate class -- with a closure, passing the appropriate argument.
For example:

user=> (class map)
clojure.core$map
user=> (.newInstance (class map))
#<core$map clojure.core$map@aea288>
user=> (.invoke (.newInstance (class map)) inc [1 2 3 4 5])
(2 3 4 5 6)

So, to embed map (not 'map or 'clojure.core/map, but map itself) in
(eval `(~map inc [1 2 3 4 5])) you could generate the bytecode for
this Java:

(new clojure.core$map()).invoke(reference-to-inc-function,
reference-to-1-2-3-4-5-vector);

With a closure, it's slightly more complicated:

user=> (def q (class (let [x 3] (fn [y] (+ x y)))))
#'user/q
user=> q
user$fn__1501$fn__1502
user=> (.invoke (.newInstance (first (.getConstructors q)) (to-array
[{} 4])) 42)
46

Note that instantiating the class with 4 gives us an add-4-to function
instead of the original add-3-to function here. To actually convert
that particular closure into code that reproduces it, the array of
constructor arguments needs to be [{} 3]. (I'm not sure what the map
argument is used for, but it seems to work when left empty, though not
when omitted entirely. The compiler can always give closure objects
metadata amounting to the correct [{} 3] or whatever needed to
recreate the closure object by calling its class's constructor.

The original problem *can* reappear with one step of regress *if* the
constructor arguments include something the compiler doesn't knows how
to turn into bytecode -- i.e. (eval (atom {})) still won't work, and
so neither will (eval (let [x (atom {})] (fn [y] (reset! x y)))), but
(eval (let [x 3] (fn [y] (+ x y)))) should work at that point as 3 is
something that can already be turned into bytecode.

Of course, for these trivial examples it's easy enough to turn the
function or closure into a quoted expression that constructs it, such
as (eval '(let [x 3] (fn [y] (+ x y)))). Where we run into problems is
when we want to embed references to already-existing functions or
other objects that lack global names. (There's an ugly workaround
involving explicitly calling intern; you create a dummy namespace with
a var holding the object, and then eval code that refers to that var
by fully-qualified name in order to retrieve the object.)

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