Constructing a packet header I am currently working on writing a library for use with LIFX light bulbs over their UDP API. I have written the code for creating the packet headers, but I'm unsure whether it is the most idiomatic approach, as I am new to Clojure. Please tell me what you think about the following code.

(ns lifx-lib.packet.header
  (:require [clj-struct.core :as struct]))

(def protocol-num 1024)
(def header-size 36)

(defn- frame-header
  [size tagged source]
  (struct/pack
    "&lt;2HI"
    size
    (bit-or protocol-num 0x1000 (if tagged 0x3000 0x0000))
    source))

(defn- address-header
  [address resp ack sequence]
  (struct/pack
    "&lt;2Q"
    address
    (bit-or
      (if resp (bit-shift-left 1 48) 0)
      (if ack (bit-shift-left 1 49) 0)
      (bit-shift-left sequence 56))))

(defn- protocol-header
  [type]
  (struct/pack "&lt;Q2H" 0 type 0))

(defn create-header
  [params]
  (let [
      size     (:size params 0)
      tagged   (:tagged params true)
      source   (:source params 0)
      address  (:address params 0)
      resp     (:resp params false)
      ack      (:ack params false)
      type     (:type params 0)
      sequence (:sequence params 0)]
    (into []
      (concat
        (frame-header size tagged source)
        (address-header address resp ack sequence)
        (protocol-header type)))))
Looks pretty good to me.  There are a few shortcuts you could add if you don't mind using the Tupelo Clojure library.  One of them is vals->map
and with-map-vals.  

Called with a list of symbols like (vals-&gt;map a b c) returns a map like {:a a :b b :c c}:

(let [a 1
      b 2
      c 3]
  (vals-&gt;map a b c))  ;=&gt;  {:a 1 :b 2 :c 3} }


Given a map like {:a 1 :b 2 :c 3} (such as generated by (vals-&gt;map a b c)), performs safe let destructuring using grab like:

   (let [some-map  {:a 1 :b 2 :c 3} } ]
     (with-map-vals some-map [a b c]
        (+ a b c)))  ;=&gt;  6


with-map-vals is safe for typos since grab will throw if the requested key is not present. See vals->map for simple creation of labeled data maps.

For default values, I think the simplest way is like this:

  (let [defaults {:a 1 :dir :up :name "joe"}
        v1       {:a 2}
        v2       {:a 9 :dir :left :name "sally"}]
    (spyx (into defaults v1))
    (spyx (glue defaults v2))


There is also the pure clojure way:

; Pure clojure way
(let [{:keys [a dir name ] } v1]
  (spyx :pure-1 (vals-&gt;map a dir name)) )

; can't use `defaults` here, need map with keyword =&gt; symbol
(let [{:keys [a dir name] :or {a 1 dir :up name "joe"}} v1] 
  (spyx :pure-2 (vals-&gt;map a dir name)))

(let [{:keys [a dir name ] :or {a 1 dir :up name "joe"}} v2] 
  (spyx :pure-1 (vals-&gt;map a dir name)) )


See also Clojure Destructuring.



Lastly, instead of concat I like to use glue.

; Glue together like collections:
(is (= (glue [ 1 2] '(3 4) [ 5 6] )       [ 1 2 3 4 5 6 ]  ))   ; all sequential (vectors &amp; lists)
(is (= (glue {:a 1} {:b 2} {:c 3} )       {:a 1 :c 3 :b 2} ))   ; all maps
(is (= (glue #{1 2} #{3 4} #{6 5} )      #{ 1 2 6 5 3 4 }  ))   ; all sets
(is (= (glue "I" " like " \a " nap!" )   "I like a nap!"   ))   ; all text (strings &amp; chars)

; If you want to convert to a sorted set or map, just put an empty one first:
(is (= (glue (sorted-map) {:a 1} {:b 2} {:c 3})   {:a 1 :b 2 :c 3} ))
(is (= (glue (sorted-set) #{1 2} #{3 4} #{6 5})  #{ 1 2 3 4 5 6  } ))




Lastly, I wouldn't bother with defn-  It just makes testing harder and provides little value.  If you have non-public functions that you don't want in the documentation (via the lein-codox plugin), you can add metadata to the function like:

(defn ^:no-doc my-helper-fn
  ...)