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

Reply via email to