I have a clojure map. Name it opts
. I know 2 ways to get values from this map such that if there is no key get some default value:
(let [opts {}
title-1 (or (:title opts) "Default title")
title-2 (:title opts "Default title")]
(println title-1 title-2))
I saw some libraries (quil, incanter) use first approach with or
. It seems to me that second approach is more concise and cleaner. Is there advantages in the first approach?
Disadvantage of using (or (:title opts) "Default value")
is that we cannot pass false
and nil
as values anymore, default value is always used in this case.
Maps are represented as alternating keys and values surrounded by { and } . When Clojure prints a map at the REPL, it will put `,'s between each key/value pair. These are purely used for readability - commas are treated as whitespace in Clojure. Feel free to use them in cases where they help you!
A map is a collection that holds key-value pairs. The values stored in the map can be accessed using the corresponding key. In Clojure there are two types of maps: Hash maps. Sorted maps.
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.
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With