On Nov 16, 11:53 am, Ben Smith-Mannschott <bsmith.o...@gmail.com>
wrote:
> On Wed, Nov 16, 2011 at 17:28, Ben Mabey <b...@benmabey.com> wrote:
> > Hi,
> > I would like to be able to add metadata to arbitrary java objects that have
> > already been instantiated.  I know that you can use proxy to add metadata to
> > objects that you create but in my case the object already exists (i.e. it is
> > returned from another method call outside of my control).
>
> > It seems like the best solution would be to create a delegate class/object
> > that wraps the original one.  Being able to write something like this would
> > be ideal:
>
> > (defn wrap-with-meta
> >  ([obj]
> >     (wrap-with-meta obj nil))
> >  ([obj meta]
> >   (delegate obj
> >             clojure.lang.IObj
> >             (withMeta [this new-meta] (wrap-with-meta obj
>
> >  new-meta))
> >             (meta [this] meta))))
>
> > The delegate function would operate very similar to proxy, but instead of
> > taking a class it takes an object.  A class would be created that extends
> > the object's class, just like a class is generated for proxy. However,
> > instead of stubs being generated that call super the stubs would delegate to
> > the given object.  (Also note, I am also using reify-like syntax since I
> > prefer that to the syntax in proxy.)
>
> > I'm curious to hear people's thoughts on this approach.  I'd also be really
> > interested to see how other people have addressed this in the past (I doubt
> > I'm the first one to run into this).
>
> > I know that clojure mentality is to avoid wrappers, but I don't see this as
> > being much different than what proxy already does.  Of course, I may be
> > missing something... if so, please enlighten me. :)
>
> > Thanks,
> > Ben
>
> Here's an approach that may be of use: don't store the metadata in a
> Map instead of decorating the Object with it. This map should use
> object identity, not equality and should hold its keys weakly so that
> it prevent collection of  objects that otherwise would be garbage.
> Safe to use concurrently would also be a plus. Conveniently, Google's
> Guava library provides such a thing. Here's a sketch:
>
> == project.clj ========================================
>
> (defproject pojometa "0.0.1-SNAPSHOT"
>   :dependencies [[org.clojure/clojure "1.3.0"]
>                  [com.google.guava/guava "10.0.1"]])
>
> == src/pojometa/core.clj ==============================
>
> (ns pojometa.core
>     (:import [com.google.common.collect MapMaker]))
>
> (def meta-map
>     (-> (MapMaker.) .weakKeys .makeMap))
>
> (defn meta* [o]
>     (if (instance? clojure.lang.IMeta o)
>         (clojure.core/meta o)
>         (.get meta-map o)))
>
> (defn with-meta* [o m]
>     (if (instance? clojure.lang.IMeta o)
>         (clojure.core/with-meta o m)
>         (do (.put meta-map o m)
>             o)))
>
> == usage ==============================================
>
> pojometa.core=> (def o (Object.)) ;; arbitrary java object
> pojometa.core=> (meta* (with-meta* o {:foo true}))
> {:foo true}
>
> ;; also "does the right thing" for Clojure types that
> ;; already know how to have metadata.
>
> pojometa.core=> (meta* (with-meta* {} {:bar 1}))
> {:bar 1}

(def o (Object.))

(def om (with-meta* o {:foo true}))

(def whatever (with-meta* o {:foo false}))

(meta* om) ;=> {:foo false}

Doesn't really support Clojure's concept of metadata if it's shared
global mutable state.

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