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