开发者

How to set default values for fields in records in Clojure?

开发者 https://www.devze.com 2023-02-24 05:43 出处:网络
I am creating records in Clojure and would like to set some fie开发者_StackOverflowlds up with a default value. How can I do this?Use a constructor function.

I am creating records in Clojure and would like to set some fie开发者_StackOverflowlds up with a default value. How can I do this?


Use a constructor function.

(defrecord Foo [a b c])

(defn make-foo
  [& {:keys [a b c] :or {a 5 c 7}}]
  (Foo. a b c))

(make-foo :b 6)
(make-foo :b 6 :a 8)

Of course there are various variations. You could for example require certain fields to be non-optional and without a default.

(defn make-foo
  [b & {:keys [a c] :or {a 5 c 7}}]
  (Foo. a b c))

(make-foo 6)
(make-foo 6 :a 8)

YMMV.


You can pass initial values to a record pretty easily when you construct it though an extension map:

(defrecord Foo [])

(def foo (Foo. nil {:bar 1 :baz 2}))

In light of this, I usually create a constructor function that merges in some default values (which you can override as you want):

(defn make-foo [values-map]
  (let [default-values {:bar 1 :baz 2}]
    (Foo. nil (merge default-values values-map))))

(make-foo {:fiz 3 :bar 8})
=> #:user.Foo{:fiz 3, :bar 8, :baz 2}


After having the same question, I ended up wrapping the defrecord and the factory function up into a single definition using a macro.

The macro:

(defmacro make-model
  [name args & body]
  (let [defaults (if (map? (first body)) (first body) {})
        constructor-name (str/lower-case (str "make-" name))]
    `(do (defrecord ~name ~args ~@(if (map? (first body)) (rest body) body))
         (defn ~(symbol constructor-name)
           ([] (~(symbol constructor-name) {}))
           ([values#] (~(symbol (str "map->" name)) (merge ~defaults values#)))))))

Usage

(make-model User [firstName lastName] {:lastName "Smith"})
=> #'user/make-user
(make-user {:firstName "John"})
=> #user.User{:firstName "John", :lastName "Smith"}
0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号