Printing and reading a function

3 views
Skip to first unread message

Jurjen

unread,
Jun 25, 2009, 10:54:47 PM6/25/09
to Clojure
I've been trying to do some generic work on serializing clojure
objects, and am much impressed with how easy and functional it is, as
well as how easy it is to extend this to other objects (eg Java
classes).

However, I seem to come a little unstuck when it comes to functions /
closures. I realise capturing the state of the environment is going to
be tricky at best, but at the moment I'm trying to extend the basic
defn macro to capture the code used to define the function.
I then tried using print-dup to print the source string from the meta
data, but this is where things stop working.

As far as I can see from the clojure/core-print.clj file, doing a
(binding [*print-dup* true] (pr-str function)) should print out the
meta-data of the function as well, but this does not seem to be
happening. I want this to happen, as I need access to the metadata to
dump the code used instead of the default #=(user
$eval__17$function__19. ) string.

Essentially my new version of defn is a basic wrapper around defn:
(defmacro mkfn [nm args func]
(list 'defn nm {:source (str "(mkfn " nm " " args " " func ")")}
args func))

so when you do (mkfn test1 [a b] (+ a b)) you get out a function test1
with a metadata :source tag (meta (var test1))
{:ns #<Namespace user>, :name test1, :file "NO_SOURCE_PATH", :line
439, :arglists ([a b]), :tag "sometag", :source "(mkfn test1 [a b] (+
a b))"}

I tried to override the print-dup for clojure.lang.Fn to check for
the :source meta-data tag, but I keep getting NullPointerExceptions.
Anyone got any ideas? Am I missing something obvious?

(defmethod print-dup clojure.lang.Fn [o w]
(if (meta o) (.write w (:source (meta o)))
(print-ctor o (fn [o w]) w)))

Chouser

unread,
Jun 26, 2009, 2:54:54 AM6/26/09
to clo...@googlegroups.com
On Thu, Jun 25, 2009 at 10:54 PM, Jurjen<jurjen....@gmail.com> wrote:
>
> Essentially my new version of defn is a basic wrapper around defn:
> (defmacro mkfn [nm args func]
>  (list 'defn nm {:source (str "(mkfn " nm " " args " " func ")")}
> args func))
>
> so when you do (mkfn test1 [a b] (+ a b)) you get out a function test1
> with a metadata :source tag (meta (var test1))
> {:ns #<Namespace user>, :name test1, :file "NO_SOURCE_PATH", :line
> 439, :arglists ([a b]), :tag "sometag", :source "(mkfn test1 [a b] (+
> a b))"}
>
> I tried to override the print-dup for clojure.lang.Fn to check for
> the :source meta-data tag, but I keep getting NullPointerExceptions.
> Anyone got any ideas? Am I missing something obvious?

It may not be obvious, but your mkfn macro is setting the
metadata of the Var that contains the fn, not the fn itself.
This is why to see the meta you had to say
(meta (var test1)) intead of just (meta test1).

This also means that if you do (pr-str test1), pr-str has no
way to get to the metadata. When this expression is
evaluated, the symbol 'test1' is resolved to the Var, then
the Var is deref'ed to the fn, and finally that fn is passed
to pr-str. There's no pointer from the fn back to the Var,
which makes sense if you think about anonymous fns, multiple
Vars pointing to the same fn, multiple threads having
different fns for the same Var, etc.

I can only think of two options for you. By far the easiest
would be to print the Var rather than the fn:

(binding [*print-meta* true] (pr-str (var map)))
or
(binding [*print-meta* true] (pr-str #'map))

The other option would be to attach your metadata to the fn
itself. This is a bit tricky because fns cannot be
duplicated in the way that would be necessary to immutably
"change" their metadata. And although you can attach
metadata to a fn instance (a.k.a. closure) when it is
created, there's no syntax for this. However, it is
possible to do with careful use of 'proxy'.

...but even once you've captured the code used to define
a fn, I'm not sure how much good it's going to do you. That
metadata won't include anything about the lexical
environment in which the fn was defined, the values of any
closed-over locals, etc.

--Chouser

Reply all
Reply to author
Forward
0 new messages