Default value in clojure maps

This is what the :or key is used for when map destructuting.

(defn f [{:keys [title-1 title-2] :as opts
          :or {title-1 "default-1" title-2 "default-2"}}]
  (println opts)
  (println title-1)
  (println title-2))

which gives you

> (f {})
{}
default-1
default-2
nil

> (f {:title-1 "foo"})
{:title-1 foo}
foo
default-2
nil

> (f {:title-2 "bar"})
{:title-2 bar}
default-1
bar
nil

> (f {:title-1 "foo" :title-2 "bar"})
{:title-1 foo, :title-2 bar}
foo
bar
nil

A crucial difference between (or (:key hash) default) and (:key hash default) is the fact that the former evaluates default only if it is necessary. In the latter case it is always evaluated. Therefore you should use or if an evaluation of default is expensive.

Another difference becomes apparent when your hash contains values which are false in a boolean context. In cases of such values (or (:key hash) default) will be evaluated to default instead of false or nil which you expect. In contrast to the or expression, (:key hash default) will yield correct results. As a side note, think twice before storing nil as values in a hash.

Fine, those were the important differences. Now let's move to minor ones.

(or (:title opts) "Default title")

is expanded by the reader to

;; Redacted for the sake of brevity.
(let* [x (:title opts)]
  (if x
    x
    "Default title"))

Arguably, it is less efficient than to simply evaluate

(:title opts "Default title")

Of course without any benchmarks it is hard to estimate the difference is speed, however I believe that it should be marginal.

On the other hand, at first glance (or (:key hash) :default) seems to be easier to understand for someone not used to the (:key hash :default) idiom. Consider programmers coming from other languages. In Ruby for instance the typical approach to handling a non existant element of a hash is

val = hash[:key] || :default

Hence, the first expression might be easier to parse by humans not accustomed to certain Clojure's idioms.

Tags:

Clojure

Map