Created
September 19, 2012 15:32
-
-
Save cemerick/3750288 to your computer and use it in GitHub Desktop.
Extending defrecord types in Clojure
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
;; Records are just types that provide default implementations of certain | |
;; key interfaces that allow them to stand in for maps. | |
;; This set of interfaces/protocols is not closed though; you can certainly make them | |
;; useful in places where maps aren't, e.g. w/ sequential destructuring: | |
=> (defrecord Point [x y] | |
clojure.lang.Indexed | |
(nth [_ i] (case i 0 x 1 y | |
(throw (IndexOutOfBoundsException.)))) | |
(nth [_ i default] | |
(case i 0 x 1 y | |
default))) | |
user.Point | |
=> (let [[a b] (Point. 1 2)] | |
(+ a b)) | |
3 | |
;; (Further examples can be found in http://clojurebook.com ;-) | |
;; The macro to generalize the above for records that define any number of slots | |
;; is fairly simple, and left as an exercise. |
I think I've got a macro to automate this working based on your snippet
(defrec
, rhymes with vec
because it makes something you can index into):
(defmacro defrec [name args & body]
(let [indexed-args (interleave (iterate inc 0) args)]
`(defrecord ~name ~args
clojure.lang.Indexed
(~'nth [~'_ ~'i]
(case ~'i
~@indexed-args
(throw (IndexOutOfBoundsException.))))
(~'nth [~'_ ~'i ~'default]
(case ~'i
~@indexed-args
~'default))
~@body)))
(let [p (->Point 10 20)
[x y] p]
(println y x))
;=> 20 10
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Yeah, I figured there was a protocol you could extend and tried to find it by digging through the Clojure source a bit but couldn't uncover it. Apparently
clojure.lang.Indexed
was what I was looking for. Is there a listing of the built-in protocols anywhere?And I wish this were part of the language itself. Clojure may not implement records as sequential but to the user they are. Clojure gives you a
(->Point x y)
style constructor function that uses the ordering of the attrs you gave it (actually so does the Java-stylePoint.
one).