GENIOUS idea Laurent ;) Extremely terse and uniform. Also because I switched the implementation to use multimethods, performance has jumped quite a bit. In fact if you memoize find-accessors, the code is only a little bit slower than update-in/assoc-in/get-in! The defset and defget macros now check to make sure their definition only defines 2 or 1 arguments respectively. find-accessor throws an error if you attempt to use an accessor in acc-in that hasn't been previously defined .
The code is for now hosted here http://github.com/swannodette/accessors/tree/master. Does anybody care to see this in contrib? If so I'll send in my CLA :) (comment (accessors x y z) ; {:x 4} (acc-x {} 4) ; 4 (acc-x {:x 4}) (defset x [m v] (assoc (assoc m :new "new") :x v)) ;; {:new "new", :x {:y {:z 1}}} (acc-in {:x {:y {:z 0}}} '(x y z) 1) ;; 0 (acc-in {:x {:y {:z 0}}} '(x y z)) ; {:x 4, :new "new", :foo 9} (let [z {:foo 9}] (acc-x z 4)) ; 10 (let [y {:z 10}] (acc-z y)) ; [0 1 2] (map acc-x [{:x 0}, {:x 1}, {:x 2}]) ; performance ---------- ; ~180ms 2.53ghz MBP Core2Duo (time (dotimes [x 100000] (assoc-in {:x {:y {:z 0}}} [:x :y :z] 1))) ; ~700ms 2.53ghz MBP Core2Duo (time (dotimes [x 100000] (acc-in {:x {:y {:z 0}}} '(x y z) 1))) ; ~300ms 2.53ghz MBP Core2Duo (def find-accessor (memoize find-accessor)) (time (dotimes [x 100000] (acc-in {:x {:y {:z 0}}} '(x y z) 1))) ) On Sun, Apr 26, 2009 at 6:27 PM, Laurent PETIT <laurent.pe...@gmail.com>wrote: > > Hello, > > Maybe you should consider creating a single function with 2 arities: > with one argument, it's the getter, with two arguments, it's the > setter (that returns the new type) ! > > > (prop-foo obj) ; --> returns the property prop-foo > (prop-foo obj newval) ; --> returns a new version of obj with prop-foo > property set to newval > > Terse and uniform :-) > > ? > > > > 2009/4/26 David Nolen <dnolen.li...@gmail.com>: > > You're right. The following includes code for handling this case via > setin > > and getin. I've also ditched macros, because that code couldn't support > new > > lexical scopes in the setter/getter definition. setin getin support works > by > > dynamically resolving getters and setters, thus this is slower than > direct > > access via assoc-in and get-in. > > Personally I have to say now that this is written, I prefer the terseness > > of: > > (set-x some-map v) > > over > > (assoc some-map :x v) > > This > > (get-x some-map) > > isn't as nice as > > (:x map) > > But it's also more explicit about how the map is being used- that a level > > indirection is required because the implementation might change. > > ;; ========================================== > > (comment > > (accessors x y z) > > ; {:x 4} > > (set-x {} 4) > > (defset x [m v] > > (assoc (assoc m :new "new") :x v)) > > ;; {:new "new", :x {:y {:z 1}}} > > (setin {:x {:y {:z 0}}} '(x y z) 1) > > ;; 0 > > (getin {:x {:y {:z 0}}} '(x y z)) > > ; {:x 4, :new "new", :foo 9} > > (let [z {:foo 9}] > > (set-x z 4)) > > ; 10 > > (let [y {:z 10}] > > (get-z y)) > > ; [0 1 2] > > (map get-x [{:x 0}, {:x 1}, {:x 2}])) > > (defn setter* [sym] > > `(defn ~(symbol (str "set-" sym)) [~'m ~'v] > > (assoc ~'m ~(keyword (str sym)) ~'v))) > > (defn getter* [sym] > > `(defn ~(symbol (str "get-" sym)) [~'m] > > (~(keyword (str sym)) ~'m))) > > (defmacro defset [sym args & forms] > > (let [set-sym (symbol (str "set-" sym))] > > `(defn ~set-sym [...@args] > > ~...@forms))) > > (defmacro defget [sym args & forms] > > (let [get-sym (symbol (str "get-" sym))] > > `(defn ~get-sym [...@args] > > ~...@forms))) > > (defn find-accessor [sym acc-type] > > (let [ns (or (namespace sym) > > (str *ns*)) > > sym-name (name sym)] > > (find-var (symbol ns (str (name acc-type) "-" sym-name))))) > > (defn setin [m [sym & syms] v] > > (let [setter (find-accessor sym :set) > > getter (find-accessor sym :get)] > > (if syms > > (setter m (set-in (getter m) syms v)) > > (setter m v)))) > > (defn getin [m [sym & syms]] > > (let [getter (find-accessor sym :get)] > > (if syms > > (getin (getter m) syms) > > (getter m)))) > > (defmacro accessors [& syms] > > `(do > > ~@(map setter* syms) > > ~@(map getter* syms))) > > > > On Sat, Apr 25, 2009 at 4:42 AM, MattH <mbhut...@gmail.com> wrote: > >> > >> It's worth considering how *nested* accessors would work in the > >> context of immutability. > >> > >> The nested maps approach works really nicely, due in part to functions > >> like assoc-in: > >> > >> ; From Mark Volkmann's tutorial > >> (assoc-in person [:employer :address :city] "Clayton") > >> > >> What would the above update look like if 'address' was accessed using > >> functions like get-address and set-address? > >> > >> Functions like assoc-in clearly rely on a uniform way of getting/ > >> setting fields (i.e. maps). > >> > >> My *hunch* is that the right avenue is to extend/implement clojure's > >> map classes if injecting behaviour is ever necessary. (A standard/ > >> supported way to do this would be nice.) > >> > >> I'd be happy using constructor functions like (make-complex-number) > >> and (make-person) which can hide detail of their implementations, but > >> I'd also like to benefit from all that goes with the idiomatic use of > >> maps. > >> > >> (My 2c) > >> > > > > > > > > > > > > > --~--~---------~--~----~------------~-------~--~----~ 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 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 -~----------~----~----~----~------~----~------~--~---