On Apr 20, 2:17 pm, Mark Engelberg <mark.engelb...@gmail.com> wrote:
> On Mon, Apr 20, 2009 at 1:27 PM, Bradbev <brad.beveri...@gmail.com> wrote:
> > If you promise that
> > functions will accept and return maps with certain keys, then you must
> > keep that promise moving forward.
>
> I think you're missing part of the point of the original post.  You
> don't really want to promise that your functions accept and return
> maps with certain keys; you want to promise that your functions accept
> and return objects that *act* like maps with certain keys.  Promising
> a map is too specific.  You merely want to promise something that
> behaves like a map.  Of course, the simplest thing that acts like a
> map is a map, so in early stages of development, you're certainly
> going to use maps.  But does such a thing scale when you decide you
> need something more complex?
Yes, and I'd say that anything that acts exactly like a map
(immutable, constant time lookup, etc) is fine to stand in place of
Clojure's map.  However, I still think that the keys in those maps and
their expected values are part of the public API.  Having a special
map that can fake having certain keys is fine, but perhaps not the way
I'd do it.
If I were to write a public library I would carefully minimize the
information that travels out of my API by not actually exposing the
internal maps.  Initially, that could be as simple as taking an
internal representation & removing non-public keys from it - I am a
big fan of having very small public APIs, & just handing out internal
structures is not a good idea.  Later on when I radically change my
internal data structures my conversion functions might get a little
more complex.
In the case of splitting a single :name into :first-name :second name,
I would need to create a function like
(defn convert-record-v1->v2 [v1-record]
  (let [[first-name last-name] (split-single-name (:name v1-record))
    (dissoc (assoc v1-record :first-name first-name :last-name last-
name) :name)))

For every API v1 function that I am preserving I convert my input maps
to the new internal form.  For every outgoing API v1 function I need
to do the reverse.

There may be performance implications for doing this conversion, but
that is a choice the client needs to make - do they stick with v1
features, or go to v2 & try to use the backward compatibility layer.

Of course, clients of the library can probably quite easily hook your
internal maps if they really want to, but at that stage they are on
their own & should not expect to work with new versions of the
library.

>
> In the original example, the data has grown from a map that actually
> contains a :name to something that retrieves a :name indirectly by
> concatenating :first-name and :last-name.  This is a perfectly
> reasonable example of the way that data representations grow and
> change over time.
It is perfectly reasonable, but it is a little much to expect to be
able to mix and match APIs both internally and externally.  In can
imagine consistency problems even in this simple case.  How do you
handle all three keys (:name, :first-name :last-name) having values
that are inconsistent with each other?
I can imagine that code getting ugly.

Cheers,
Brad

>
> Of course, if you had programmed your entire app with getters and
> setters from the outset, making such a modification is relatively
> straightforward (see footnote *).  But most people aren't going to
> program that way from the outset.  They are going to use maps because
> that's the easiest thing, and that means they will get and set things
> using get, assoc, and the syntactic function-like shortcuts for
> retrieving things from maps.
>
> (Footnote * - Well maybe it's not so easy to convert something that
> uses getters and setters from the outset but still uses a map to hold
> the data.  maps are very "leaky abstractions", and even if you provide
> getters and setters, it's entirely possible that users might
> intentionally or unintentionally use something map-specific, rather
> than adhering to the level of indirection that is needed to make a
> conversion easy.)
>
> But then when you eventually want to alter your data representation,
> then what do you do?  It's certainly possible to make your name
> objects some sort of proxy for the associative interface, function
> interface, and anything else relevant to maps, and special case the
> getting of :name, but that's quite cumbersome.  Is there a simple way
> to do this extension?  Not really, and I think this is a legitimate
> concern.
--~--~---------~--~----~------------~-------~--~----~
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