Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Default value in clojure maps

Tags:

map

clojure

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.

like image 205
Mikita Belahlazau Avatar asked Dec 11 '12 09:12

Mikita Belahlazau


People also ask

How does map work Clojure?

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!

What is map in Clojure?

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.


2 Answers

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.

like image 81
Jan Avatar answered Oct 05 '22 10:10

Jan


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
like image 38
beoliver Avatar answered Oct 05 '22 12:10

beoliver