Clojure : How to instantiate a record from a map

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)

My first working (and trivial) Clojure program

I would like to share with you the joy of being able to finally make something working in Clojure.
This is a very simple program solving a very simple problem usually described in any IT University: palindrome detection.
My solution is not the most elegant and short one but it gave me the possibility to practice with Vectors.
Obviously any suggestion is more than welcome, thank you!

(defn is-letter [letter]
  (and (> (int letter) (int \A)) (< (int letter) (int \z))))

(defn check-palindrome
  ([word] (let [length (count (seq word))
                letter-vector (vec (filter #(is-letter %1) (seq (.toUpperCase word))))]
                (check-palindrome  letter-vector 0 (- (count letter-vector) 1) )
          )
  )
  ([letters start end ]  (cond
                           (< end start) (Exception. "The string is NOT a palindrome")
                           (not (= (letters start) (letters end))) (Exception. "The word is NOT a palindrome")
                           (or (= start end) (= 1 (- end start))) (println "The string is a palindrome")

                     :else  (recur letters (inc start) (dec end)))
   )
 )
 
(check-palindrome "Murder, for a jar of red rum")