After playing around, I think it would be ideal that if you *don't*
specify clojure.lang.IPersistentMap as an interface, you still get an
implementation of assoc that works only with keys already in the type.
In other words, you can't add a new field, and you can't call dissoc
to remove it, but you still have the ability to say, "Give me another
object just like this one, but with such-and-such value changed".
In other words, I'm suggesting that if you do something like:
(deftype Posn [x y])
(def p (Posn 1 2))
You should be able to do (assoc p :x 3), you should get back #:Posn{:x
1, :y 2}, however
(assoc p :z 3) will not work, and (dissoc p :x) would not work.
I'm mainly suggesting this because deftypes seem a bit of a pain to
work with without some way to create an updated object, whereas for
many purposes, the full-blown map capabilities can make your code more
fragile (for example, if you really mean a Posn to contain an :x and
:y, no more, no less).
Thoughts?
You meant that you get back #:Posn{:x 3, :y 2}, right? Sounds
reasonable to me.
> --
> You received this message because you are subscribed to the Google
> Groups "Clojure" group.
> To post to this group, send email to clo...@googlegroups.com
> Note that posts from new members are moderated - please be patient with your
> first post.
> To unsubscribe from this group, send email to
> clojure+u...@googlegroups.com
> For more options, visit this group at
> http://groups.google.com/group/clojure?hl=en
> After playing around, I think it would be ideal that if you *don't*
> specify clojure.lang.IPersistentMap as an interface, you still get an
> implementation of assoc that works only with keys already in the type.
> In other words, you can't add a new field, and you can't call dissoc
> to remove it, but you still have the ability to say, "Give me another
> object just like this one, but with such-and-such value changed".
I agree that's often a useful behaviour to have, but on the other hand I would not want it automatically, because for some types I don't want anyone to mess around with my objects at a field-by-field level. For example if there are invariants among several field values that need to be maintained.
Konrad.
Rich's stated reason against this is that he wants to be able, some
day, to implement maps and map-like things with deftype.
-SS
Hmmm, if so, then there's a bit of a design tension between that goal
and the goal of "prefer deftype to defstruct in all cases", which
implies that deftype is going to play an important everyday role in
the Clojure programmer's toolkit (not just a behind-the-scenes thing)
and therefore, it's very important to optimize for the "common case".
I find it hard to believe that implementing maps and map-like things
will be the common case.
It sounds to me like there are a few options here, depending on what
you view the common case to be:
1. Do nothing and leave things as-is. This forces the user to choose
between no easy update ability, and full-blown map behavior where you
can add/remove keys however you like.
2. Create a way to opt-in to just the "assoc of deftype's built-in keys".
3. By default, allow "assoc of deftype's built-in keys", but provide
a way to opt out of this.
I think #1 is the worst option. I definitely prefer #3 over #2
because I believe it is the most common case, but I can imagine a case
could be made for #2 over #3. However, if it's going to be an
"opt-in" system for something as fundamental as assoc on built-in
keys, I think it would be great if there were a syntactically
beginner-friendly way to accomplish this. For a beginner coming to
Clojure, I think it will feel too much like a "magic incantation" to
have to explicitly mix-in some kind of implementation of
IPersistentMap that throws errors for all but the "assoc of built-in
keys" case, since beginners are unlikely to need or want to know how
Clojure's interfaces are layered and implemented behind-the-scenes in
order to get a "(persistent) structure with settable fields".
Has their been any community brainstorming about what the syntax could
look like for a general mechanism to specify within a deftype default
implementations or default partial implementations of various
interfaces?
extend's capability to use maps of keywordized function names to
method implementations is potentially powerful, but I wonder if it
might be nice to also add to these maps metadata which indicates a
list of interfaces/protocols known to be implemented (or partially
implemented) by the methods in that map. This might allow for a
syntax within deftype where after defining the fields of the type, you
can specify a bunch of these "mixin maps", and it can use the metadata
to automaticaly categorize them into the appropriate interface
implementation.
> Actually, the more I think about it, the more I feel like deftype's
> "specify clojure.lang.IPersistentMap as an interface with no
> implementation, and you'll get a default implementation" seems like a
> weird exception,
I agree. The current deftype is meant to be both the primitive construct for implementing data types, and a high-performance substitute for defstruct. It would be conceptually cleaner to separate these two roles.
> Has their been any community brainstorming about what the syntax could
> look like for a general mechanism to specify within a deftype default
> implementations or default partial implementations of various
> interfaces?
I don't remember. The issue of default implementations comes up now and then, but only in the form of the question "is it possible?".
In my own applications, I have only used deftype as a primitive construct, not as a substitute for defstruct. The special status of IPersistentMap thus never bothered me. However, I do see other cases where default implementations would be useful to have. And then IPersistentMap could become just one of them.
Konrad.
I just wanted to chime in and say I agree - I am not totally happy
with how getting the map/default implementation works, and think that
possibly the two roles deftype/defstruct-replacement should be more
distinct. So, I'm working on it.
Rich