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 <[email protected]>wrote:
>
> On Fri, Apr 24, 2009 at 3:36 PM, David Nolen <[email protected]>
> 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 [email protected]
To unsubscribe from this group, send email to
[email protected]
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
-~----------~----~----~----~------~----~------~--~---