If your Clojure program is using records to encapsulate data, you know how cumbersome is to create a new instance of a record.
For example, in my little application I have a record Delivery :
(defrecord Delivery [_id userdef-id to-address from-address subj content])
Now… create an instance of delivery is a bit of a pain:
(def my-delivery (Delivery. "1223" "abc" "nico@gmail.com" "david.smith@gmail.com" "Test email" "the content" ))
The pain comes from the fact that you need to remember exactly the position of every single field of your delivery record and you have to specify nil whenever one of the record component is not available (keep reading for an example).
You know that a Delivery is inherently a map. Try this at your repl to prove it:
user# (defrecord MyRecord [_id]) |user/MyRecord user#(isa? MyRecord java.util.Map) |true
This means you can treat a Clojure record as a map, hence you can merge a record with a map.
If you want to make a record instantiation a bit handier, this is how you could do:
(def my-delivery (merge (Delivery. []) {:_id "234" :content "the content"}))
Here I’m basically creating an empty Delivery and I’m merging it with a map containing only the fields of the record I need to set.
The same declaration would otherwise be:
(def my-delivery (Delivery ["234" nil nil nil nil "the content"}))
Using the merge approach you will be able to create a new record without specifying nil components. Also you won't have to care about the order of the record fields.
You could potentially create a macro in order to make this new pattern (way of creating a new record) part of your programming language (mighty macros!).
(defmacro defdelivery [name the-map] `(def ~name (merge (Delivery. []) ~@the-map)))
This way you could create a new record using the following code:
(defdelivery my-delivery {:_id "123" :content "the content"})
Neat eh?
I am not 100% sure this approach has any drawback since I’m not a hardcore clojure developer but please don’t hesitate to comment this post in case there’s anything we all need to know about this.
Nico
EDIT: Thanks to Tylor for pointing out that what I tried to achieve in this article has actually been introduced in Clojure 1.3 (defrecord improvements)