Sometimes you want the benefits of a dynamically typed language with some of the benefits of a statically typed one.

Many languages these days encourage the user to use dictionaries or maps as a kind of loosely typed record.

These languages often differ on how they treat the situation where you look up a nonexistent key in the map.

That's because they have to conflate two different purposes.

  1. The traditional data-structurish use of maps, where the keys are usually calculated, and a nonexistent one could conceivably be valid. The classical example is a word count program. If a program looks for an unknown key in the word count map, it's reasonable to just return null, because the result will clearly be identical to nil.

  2. The record-like use of maps, where if you look up :aghe in a map with has keys :age, it's obvious a programming error has occurred.

This distinction is roughly related to the (rather outdated now) Exception/RuntimeException distinction in Java. Although the checked exception feature was such a horrible pain that many now derive from RuntimeException, the previous notion was that you avoided catching RuntimeException. The RuntimeException should kill your program, or at least the handling thread.

In the case of example 2, it's clear that (get mymap :aghe) can kill the program. However, in Clojure that expression will return nil. At some other time your code will get a wrong result. By that time, however, the stack trace may be useless.

strict-get will cause your code to break when you access a nonexistent field. This function is also known as grab in the tupelo library.

(defn grab [key_ map_]
  (let [val_ (get-in map_ [key_] ::not-found)]
    (if (= val_ ::not-found)
      (do
        (debugp "input was" map_)
        (throw (IllegalArgumentException. 
                (format "unable to grab key '%s' from map" key_))))
      val_)))

(defn strict-get [map_ key_]
  (grab key_ map_))

(defn strict-get-in [map_ path]
  (have! sequential? path)
  (reduce strict-get map_ path))

debugp is a personal debugging macro, feel free to replace it as you wish. Also, have! is an assertion from the truss library.

Personally, I'd love for this to become a Clojure idiom, I find it incredibly useful, especially when first writing a new piece of functionality. It's important to remember, however, that you always need to think about your intention when you do a map lookup. For me, strict-get is a good default, but I still use get, :foo, and all the others in all their various permutations.

I wrote this macro that's a simplistic version of map destructuring, but using strict-get instead:

(defn generate-strict-get [keyword input-sym]
  `(strict-get ~input-sym ~keyword))

(defn generate-let-pair [binding-sym keyword input-sym]
  `[~binding-sym ~(generate-strict-get keyword input-sym)])

(defn generate-bindings-vector [bindings input-sym]
  (->> (map (fn <span class="createlink">k v</span>
         (generate-let-pair k v input-sym)) bindings)
       (mapcat identity)
       vec))

;; (strict-ds-bind {foo :bar
;;                  baz :quux} input
;;                 expr)

(defmacro strict-ds-bind [bindings input & expr]
  `(let ~(generate-bindings-vector bindings input)
     ~@expr))

When you use this form, it's obvious what's happening, and the reader knows that there's a chance that the continuation may throw at this stage. If you want to get this to indent the body properly, you can use this snippet in your .emacs.

(define-clojure-indent
  (strict-ds-bind 2))

Thanks to this Stackoverflow question for the main inspiration. Apparently prismatic/plumbing also calls this safe-get.

I find this so useful that I find myself writing it in JavaScript as well, YMMV though. Python is the only mainstream language that works like this by default, as far as I know.