Skip to content

Instantly share code, notes, and snippets.

@fogus
Created August 2, 2010 16:10
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save fogus/504861 to your computer and use it in GitHub Desktop.
Save fogus/504861 to your computer and use it in GitHub Desktop.
;; stolen from http://cemerick.com/2010/08/02/defrecord-slot-defaults/
(defmacro defrecord+defaults
"Defines a new record, along with a new-RecordName factory function that
returns an instance of the record initialized with the default values
provided as part of the record's slot declarations. e.g.
(defrecord+ Foo [a 5 b \"hi\"])
(new-Foo)
=> #user.Foo{:a 5, :b \"hi\"}"
[name slots & etc]
(let [fields (->> slots (partition 2) (map first) vec)
defaults (->> slots (partition 2) (map second))]
`(do
(defrecord ~name
~fields
~@etc)
(defn ~(symbol (str "new-" name))
~(str "A factory function returning a new instance of " name
" initialized with the defaults specified in the corresponding defrecord+ form.")
[& {:as kwargs#}]
(-> (~(symbol (str name \.)) ~@defaults)
(merge kwargs#)))
~name)))
(defrecord+defaults Foo [a 1 b 2])
(new-Foo :a 42)
;=> #:user.Foo{:a 42, :b 2}
@cemerick
Copy link

cemerick commented Nov 2, 2011

Alternatively:

;; Clojure 1.3.0
=> (defrecord A [x y])
user.A
=> ((comp map->A (partial merge {:x 5})) {:y 6})
#user.A{:x 5, :y 6}

…or…

=> ((comp map->A
          (partial merge {:x 5})
          #(apply hash-map %&))
     :y 6)
#user.A{:x 5, :y 6}

@fogus
Copy link
Author

fogus commented Nov 2, 2011

Nice! HOFs FTW.

@cemerick
Copy link

cemerick commented Nov 2, 2011

Yeah; I probably wouldn't write defrecord+ today. Given the generic map factory fn, you can easily define multiple sets of defaults using the same base record type. Complecting a record type with its defaults is not necessarily the right path. ;-)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment