You have some valid points, but I think trying to come up with a solution using existing components in Clojure in order to determine if there really is a gap in Clojure's design is the best approach. I don't always use accessor macros but that's because I don't normally build up maps that are intended to be used in an "object-like" fashion. If I really did need to treat maps like objects for some reason, I would most definitely use some macros. In fact you inspired me to come with a more complete solution: (comment (accessors x y z)
; {:x 4} (set-x {} 4) (defset x [m v] (assoc (assoc m :new "new") :x v)) ; {: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-fn [{:x 0}, {:x 1}, {:x 2}])) As far as I'm concerned this covers most of the common usages of setters/getters. I made the base getter/setter a macro, so it always converts to the standard key access form in order to not add a function call. It also defines another function which can be used in mapping operations. defget, defset provide a means for changing the boilerplate getters and setters. Though one could quibble about the style (what should the accessor actually look like), this seems to me at least a starting point for a Clojure library to solve the problem right now. It may turn out that using something like this doesn't scale, but you don't know until you try right? :) Again, I'm not saying your concerns are not valid, but I still say only time will tell. The code only relies on the zip library. Apologies for any sloppiness I only spent a couple of hours on it :) (defn setter [sym] `(defmacro ~(symbol (str "set-" sym)) [~'x ~'y] `(assoc ~~'x ~~(keyword (str sym)) ~~'y))) (defn getter [sym] `(defmacro ~(symbol (str "get-" sym)) [~'x] `(~~(keyword (str sym)) ~~'x))) (defn setter-fn [sym] `(defn ~(symbol (str "set-" sym "-fn")) [~'x ~'y] (assoc ~'x ~(keyword (str sym)) ~'y))) (defn getter-fn [sym] `(defn ~(symbol (str "get-" sym "-fn")) [~'x] (~(keyword (str sym)) ~'x))) (defn defset* [sym] `(do ~(setter sym) ~(setter-fn sym))) (defn defget* [sym] `(do ~(getter sym) ~(getter-fn sym))) (defmacro defset [sym args & forms] (let [set-sym (symbol (str "set-" sym)) set-fn-sym (symbol (str "set-" sym "-fn"))] `(do (defmacro ~(symbol (str "set-" sym)) [...@args] (cons 'do (replace-syms '~forms (zipmap [~@(map #(list 'quote %) args)] [...@args])))) (defn ~set-fn-sym [...@args] ~...@forms)))) (defmacro defget [sym args & forms] (let [get-sym (symbol (str "get-" sym)) get-fn-sym (symbol (str "get-" sym "-fn"))] `(do (defmacro ~(symbol (str "get-" sym)) [...@args] (cons 'do (replace-syms '~forms (zipmap [~@(map #(list 'quote %) args)] [...@args])))) (defn ~get-fn-sym [...@args] ~...@forms)))) (defmacro accessors [& syms] `(do ~@(map defset* syms) ~@(map defget* syms))) (defn replace-syms [code kvs] (loop [loc (z/seq-zip code)] (if (z/end? loc) (z/root loc) (recur (z/next (if-let [match (some #{(z/node loc)} (keys kvs))] (z/replace loc (get kvs match)) loc)))))) On Fri, Apr 24, 2009 at 8:07 PM, Mark Engelberg <mark.engelb...@gmail.com>wrote: > > On Fri, Apr 24, 2009 at 3:36 PM, David Nolen <dnolen.li...@gmail.com> > wrote: > > Is this really so hard? > > Are you telling me that you routinely write accessors for all your > data structures in Clojure using those macros? I'll bet very few > people do this. People make use of the facilities conveniently > available to them. Unlike Scheme where define-struct gives you all > your accessors for free, Clojure does not (and why should it? It > provides a very versatile interface that applies to all associative > objects). So people will either use the built-in Clojure way, which > makes use of maps in a fairly non-extensible way, or roll their own > accessor system with varying conventions (is it get-x, or x-get, or > rational-x, namespace/x, etc.?). Either way sounds problematic to me. > > Wouldn't it be even better if you could always use Clojure's standard > accessor/setting syntax, but the meaning of this syntax could be > readily modified for certain types of objects to meet more complex > needs? > > There's a broader issue here. This thread started out with a question > from a newcomer about how to write ADTs in Clojure. I think if it's > not abundantly clear how to write an ADT in a given programming > language, and there's no consensus on the cleanest way or a set of > very clean ways to do that because most approaches require a certain > amount of "roll-your-own constructor/type > tagging/accessors/implementation hiding" machinations above and beyond > what is easily provided by the language, that's a sign that > programming in that language isn't as easy as it should be. > > Put another way, if you were working at a startup about to embark on a > rather large, ambitious programming project in Clojure that would span > many employees and many years, do you feel you have a really solid > handle on exactly how to use Clojure's facilities to architect a > long-lasting, maintainable solution? How long would you spend > developing standards for coding new data structures? What do your > constructors look like? Where do you add the type tag (in the > structure or in the metadata)? Do you hide any information, and if > so, how? Do you enforce that employees always make accessors, and if > so, what macro should they use? How much do you use multimethods to > define your interfaces, versus the Java interface/class system? How > confident are you that you'd get these decisions right the first time, > rather than making a choice (like a naive solution involving maps) > that will come back to bite you later? > > Some of these choices will exist in any language, but the situation is > even more extreme in Clojure, in part because of its newness, and in > part because of Rich's conscious decision to avoid baking in some of > these decisions into the language proper. And some of the choices are > unique to Clojure because the core constructs and interfaces are > written using a completely different set of idioms (Java classes and > extensions) than typical user code (multimethods over maps as a > polymorphic interface system), and these two approaches don't always > play well together. > > I find Clojure delightful for writing small programs, but I don't > think there's a clearcut way yet to follow programming-in-the-large > software engineering principles while sticking with existing Clojure > idioms. I think any large project would necessarily need to invent a > whole layer on top of the existing facilities, and I find that > unsatisfying. > > > I'll also point out that making maps work the way > > you propose isn't even particularly ideal because _anything_ in Clojure > can > > be a key not just keywords. > > p.{"first": "Bob", "second": "Smith"} = 5 > > Makes absolute no sense in, say, Python, but > > (assoc some-map {:first "Bob" :second "Smith"} 5) > > is perfectly valid in Clojure. How do you propose to reconcile this? > > Just asking ;) > > I'm not sure what I said that sounded like it was geared specifically > to keywords as opposed to keys in general. I can imagine various > solutions to the overall issue. Perhaps the metadata for a map can > optionally contain a mapping from keys (even complex non-keyword keys > like the one you describe) to custom getter/setter functions, and > core's get and assoc respect those overrides. Although I see no > problem with these things working on complex keys, in practice, I > suspect that if such facilities existed, they would mostly be used for > keywords in maps that are being used as an implementation for new data > types. > > But perhaps hacking more on maps is not the way to go. Maybe Clojure > needs something new, designed to make ADTs as easy to create as > possible. Keep the possibilities open, but give users one solid, > clearcut way to do the common case in a scalable way. > > > > --~--~---------~--~----~------------~-------~--~----~ 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 -~----------~----~----~----~------~----~------~--~---