I'm not caching the values, only the mapping from keywords to methods (the 
caching is per class, not per bean). It's just saving the 
introspection/reflection on each call. The values themselves are always 
obtained directly from the bean.

On Thursday, January 30, 2014 4:12:31 PM UTC+2, Jozef Wagner wrote:
>
> Hi,
>
> Not every bean is immutable, so you cannot cache its values by default. If 
> reading from the bean is expensive for you, either create a normal hash-map 
> with into {} and select-keys, or use memoize.
>
> user=> (def b (bean (java.util.Date.)))
> #'user/b
> user=> (def hm (into {} (select-keys b [:year :month :date])))
> #'user/hm
> user=> hm
> {:date 30, :month 0, :year 114}
> user=> (def mb (memoize b))
> #'user/mb
> user=> (mb :day)
> 4
>
> JW
>
>
>
> On Thu, Jan 30, 2014 at 2:35 PM, pron <ron.pr...@gmail.com 
> <javascript:>>wrote:
>
>> The bean function is a very useful Java interop feature that provides a 
>> read-only view of a Java Bean as a Clojure map.
>> As it stands, the function performs introspection on the bean's class 
>> whenever the function is called:
>>
>>
>> (defn bean
>>   "Takes a Java object and returns a read-only implementation of the
>>
>>   map abstraction based upon its JavaBean properties."
>>   {:added "1.0"}
>>
>>   [^Object x]
>>   (let [c (. x (getClass))
>>
>>         pmap (reduce1 (fn [m ^java.beans.PropertyDescriptor pd]
>>
>>                          (let [name (. pd (getName))
>>
>>                                method (. pd (getReadMethod))]
>>
>>                            (if (and method (zero? (alength (. method 
>> (getParameterTypes)))))
>>
>>                              (assoc m (keyword name) (fn [] 
>> (clojure.lang.Reflector/prepRet (.getPropertyType pd) (. method (invoke x 
>> nil)))))
>>
>>                              m)))
>>                      {}
>>                      (seq (.. java.beans.Introspector
>>
>>                               (getBeanInfo c)
>>
>>
>>                               (getPropertyDescriptors))))
>>         v (fn [k] ((pmap k)))
>>
>>         snapshot (fn []
>>                    (reduce1 (fn [m e]
>>
>>                              (assoc m (key e) ((val e))))
>>
>>                            {} (seq pmap)))]
>>
>>
>>     (proxy [clojure.lang.APersistentMap]
>>            []
>>
>>       (containsKey [k] (contains? pmap k))
>>
>>       (entryAt [k] (when (contains? pmap k) (new clojure.lang.MapEntry k (v 
>> k))))
>>
>>       (valAt ([k] (when (contains? pmap k) (v k)))
>>
>>              ([k default] (if (contains? pmap k) (v k) default)))
>>
>>       (cons [m] (conj (snapshot) m))
>>
>>       (count [] (count pmap))
>>
>>       (assoc [k v] (assoc (snapshot) k v))
>>
>>       (without [k] (dissoc (snapshot) k))
>>
>>       (seq [] ((fn thisfn [plseq]
>>
>>                   (lazy-seq
>>                    (when-let [pseq (seq plseq)]
>>
>>                      (cons (new clojure.lang.MapEntry (first pseq) (v (first 
>> pseq)))
>>
>>                            (thisfn (rest pseq)))))) (keys pmap))))))
>>
>>
>>
>>
>> I propose to cache the pmap value for each class using JDK 7's 
>> ClassValue<http://docs.oracle.com/javase/7/docs/api/>. 
>>  Here's a proposed implementation:
>>
>> (def ^:private  ^java.lang.ClassValue bean-class-value
>>
>>       (proxy [java.lang.ClassValue]
>>              []
>>         (computeValue [c]
>>
>>           (reduce (fn [m ^java.beans.PropertyDescriptor pd]
>>
>>                      (let [name (. pd (getName))
>>
>>                            method (. pd (getReadMethod))
>>
>>                            type (.getPropertyType pd)]
>>                        (if (and method (zero? (alength (. method 
>> (getParameterTypes)))))
>>
>>                          (assoc m (keyword name) (fn [x] 
>> (clojure.lang.Reflector/prepRet type (. method (invoke x nil)))))
>>
>>                          m)))
>>                    {}
>>                    (seq (.. java.beans.Introspector
>>
>>                             (getBeanInfo c)
>>                             (getPropertyDescriptors)))))))
>>
>>  
>> (defn bean
>>   "Takes a Java object and returns a read-only implementation of the
>>
>>   map abstraction based upon its JavaBean properties."
>>   {:added "1.0"}
>>
>>   [^Object x]
>>   (let [c (. x (getClass))
>>
>>         pmap (.get  bean-class-value c)
>>
>>         v (fn [k] ((pmap k) x))
>>
>>         snapshot (fn []
>>                    (reduce (fn [m e]
>>
>>                               (assoc m (key e) ((val e) x)))
>>
>>                             {} (seq pmap)))]
>>     (proxy [clojure.lang.APersistentMap]
>>
>>            []
>>       (containsKey [k] (contains? pmap k))
>>
>>       (entryAt [k] (when (contains? pmap k) (new clojure.lang.MapEntry k (v 
>> k))))
>>
>>       (valAt ([k] (when (contains? pmap k) (v k)))
>>
>>         ([k default] (if (contains? pmap k) (v k) default)))
>>
>>       (cons [m] (conj (snapshot) m))
>>
>>       (count [] (count pmap))
>>       (assoc [k v] (assoc (snapshot) k v))
>>
>>       (without [k] (dissoc (snapshot) k))
>>
>>       (seq [] ((fn thisfn [plseq]
>>
>>                  (lazy-seq
>>                    (when-let [pseq (seq plseq)]
>>
>>                      (cons (new clojure.lang.MapEntry (first pseq) (v (first 
>> pseq)))
>>
>>                            (thisfn (rest pseq)))))) (keys pmap))))))
>>
>>  
>>
>>  
>>
>> -- 
>> You received this message because you are subscribed to the Google
>> Groups "Clojure" group.
>> To post to this group, send email to clo...@googlegroups.com<javascript:>
>> Note that posts from new members are moderated - please be patient with 
>> your first post.
>> To unsubscribe from this group, send email to
>> clojure+u...@googlegroups.com <javascript:>
>> For more options, visit this group at
>> http://groups.google.com/group/clojure?hl=en
>> --- 
>> You received this message because you are subscribed to the Google Groups 
>> "Clojure" group.
>> To unsubscribe from this group and stop receiving emails from it, send an 
>> email to clojure+u...@googlegroups.com <javascript:>.
>> For more options, visit https://groups.google.com/groups/opt_out.
>>
>
>

-- 
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
--- 
You received this message because you are subscribed to the Google Groups 
"Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to clojure+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.

Reply via email to