So just to explain this a little more, recently I wanted something
record-like with custom hashing and equality for my ubergraph library.
Unfortunately, you can't do this with defrecord, and starting from deftype
and re-implementing all the map-like semantics from scratch is a total pain.

So what I did is from potemkin, I referred def-map-type, and from
potemkin.collections I referred AbstractMap.

The type definition begins like this:
(def-map-type Ubergraph [node-map allow-parallel? undirected? attrs
cached-hash]
  AbstractMap

and then you have your usual alternation of protocols and implementations.

At the end, you must implement the following:
  (get [this key default-value] ...)
  (assoc [this key value] ...)
  (dissoc [this key] ...)
  (keys [this] ...)
  (meta [this] ...l)
  (with-meta [this meta] ...)

Then, you have the flexibility to override hasheq and equiv:
  (hasheq [this] ...)
  (equiv [this other] ...)

I'm not crazy about the fact that I had to implement get, assoc, dissoc,
keys, meta, and with-meta in order to override hasheq and equiv.  It was
routine and tedious, and it is stuff that defrecord's macro does
automatically for you.  For example, my get implementation looks like this:
(get [this key default-value]
       (case key
         :node-map node-map
         :allow-parallel? allow-parallel?
         :undirected? undirected?
         :attrs attrs
         :cached-hash cached-hash
         default-value))
just taking cases on all the keywords that refer to fields in my deftype.

This means that any time you add a field to your record, you have to go
back and tweak all those method implementations, which is annoying.
Despite these complaints, it was, by far, the simplest way I was able to
find to get a record/map-like structure with custom hashing and equality.
Even though potemkin's def-map-type makes you implement those methods on
your own, it does a lot of the grunt work of fulfilling other map methods
that you probably wouldn't remember to implement everything by yourself.

Right now Clojure offers this all-or-nothing approach: do it entirely
yourself with deftype or use defrecord and lose the ability to customize.
Clojure's position statement on this is that data structures generally fall
cleanly into one of these two categories.  I find that premise to be
faulty.  The simplest "fix" would be if Clojure simply allowed one to
override the protocols which are implemented for you by defrecord, rather
than throwing an error and saying that you can't redefine one of those
protocols because they have already been implemented.  Keep the errors for
the case when you accidentally redefine the same method twice in your own
implementation, but don't call it an error if you override the default
implementation of something provided by the defrecord macro.

-- 
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
Note that posts from new members are moderated - please be patient with your 
first post.
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
--- 
You received this message because you are subscribed to the Google Groups 
"Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to clojure+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to