DEV Community

Rodrigo Flores
Rodrigo Flores

Posted on

Fallback on maps lookup on Clojure (or a gentle introduction to macros)

So, you have to deal with a map, but you can't be sure whether or not you have a value for a given key.

The easiest, and probably the most known is to use the get function (that accepts optionally a third argument which is the fallback value).

(def a-qb {:name "Josh"
           :surname "McCown"
           :team "New York Jets"})

(def another-qb {:name "Robert"
                 :surname "Griffin III"})

(get a-qb :team) ; => "New York Jets"
(get another-qb :team) ; => nil 
(get another-qb :team "Free Agent") ; => "Free Agent"
Enter fullscreen mode Exit fullscreen mode

Another way is to use the keyword as a function or even the map as a function.

(:team a-qb) ; => "New York Jets"
(:team another-qb) ; => nil 

(a-qb :team) ; => "New York Jets"
(another-qb :team) ; => nil
Enter fullscreen mode Exit fullscreen mode

I've learned that you can also specify a second argument to these cases, and if the value is not found, it will return this second argument.

(:team a-qb "Free Agent") ; => "New York Jets"
(:team another-qb "Free Agent") ; => "Free Agent" 

(a-qb :team "Free Agent") ; => "New York Jets"
(another-qb :team "Free Agent") ; => "Free Agent"
Enter fullscreen mode Exit fullscreen mode

But what if you want to raise an exception if it does not exists?

(a-qb :team (throw (Exception. )))
(a-qb :team (throw (Exception. )))
Enter fullscreen mode Exit fullscreen mode

If you call it this way, it will evaluate all parameters before the function is called (because it is a function), raising an exception regardless if the key-value pair exists or not on the map. A way to workaround this it is to use an or.

(or (a-qb :team) 
    (throw (Exception. ))
Enter fullscreen mode Exit fullscreen mode

Because the or is implemented as a macro, it will only evaluate the second sexp (throw ...) unless the first sexp is falsy (nil or false) and thus raising an exception only on this case.

Latest comments (2)

Collapse
 
sava4 profile image
Sava4 • Edited

This example should return "Free Agent" and not nil
checked in the REPL:

(:team a-qb "Free Agent") ; => "New York Jets"
(:team another-qb "Free Agent") ; => nil 

(a-qb :team "Free Agent") ; => "New York Jets"
(another-qb :team "Free Agent") ; => nil
Collapse
 
rodrigoflores profile image
Rodrigo Flores

Thanks, I've fixed them :)