Records, Types and reify

When to use what: plain maps for most data; records when you need protocol dispatch on a type with performance; deftype for low-level data structures; reify for one-off implementations.

;; defrecord: a map-like type with fields (works with all map functions)
(defrecord Person [name age])
(def ana (->Person "Ana" 30))              ; positional factory
(def bo (map->Person {:name "Bo" :age 25})); map factory
(:name ana)                                ; "Ana"
(assoc ana :email "[email protected]")               ; still a Person, with an extra key
(dissoc ana :name)                         ; dissoc'ing a field returns a plain map
(instance? Person ana)                     ; true
(= ana (->Person "Ana" 30))                ; true (value equality)
(record? ana)                              ; true

;; deftype: a low-level type, no map behavior, supports mutable fields
(deftype Counter [^:volatile-mutable n]
  clojure.lang.IDeref
  (deref [_] n)
  Object
  (toString [_] (str "Counter(" n ")")))
(def ct (Counter. 5))
@ct                                        ; 5

;; reify: an anonymous object implementing protocols/interfaces
(def greeter
  (reify
    Object
    (toString [_] "I'm a greeter")
    Describable
    (describe [_] "greets people")))
(str greeter)                              ; "I'm a greeter"

;; proxy: subclass a Java class (when an abstract class is required)
(def task
…