For anybody who's still following, I've updated accessors.clj with a new behavior. I realized assoc-in and get-in are very powerful, they work with all sorts of collections (not just maps, duh on my part), thus now if anything in the list of accessor symbols is not actually a clojure.lang.Symbol, it will switch over to assoc-in/get-in. This allows accessors to seemlessly support updating lists, vectors, etc. as well as maps that are using something other than keywords to store properties. A non silly example:
(ns accessors-example (:use accessors) (:use clojure.contrib.str-utils)) (accessors first last) (defget name [m] (str (acc-first m) " " (acc-last m))) (defset name [m v] (let [[first last] (re-split #" " v)] (-> m (acc-first first) (acc-last last)))) (acc-name {:first "Bob", :last "Smith"}) ; -> "Bob Smith" (acc-name {} "Bob Smith") ; -> {:first "Bob", :last "Smith"}'s On Sun, Apr 26, 2009 at 10:27 PM, David Nolen <dnolen.li...@gmail.com>wrote: > 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 -~----------~----~----~----~------~----~------~--~---