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