Created
March 6, 2017 16:28
-
-
Save pjstadig/25c8a775f5403a2f0e3ad94f81ef58ff to your computer and use it in GitHub Desktop.
In Clojure you can fetch items from a map three different ways. Which should you use when?
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(ns maps.core) | |
;; In Clojure you can fetch items from a map three different ways. Which should | |
;; you use when? | |
(= "bar" ({:foo "bar"} :foo)) ; map as function | |
(= "bar" (:foo {:foo "bar"})) ; key as function | |
(= "bar" (get {:foo "bar"} :foo)) ; `get` as function | |
;; <INCIDENTALLY> | |
;; Incidentally, all three of these can take a default value to return if the | |
;; key is not present in the map. | |
(= "not-here" ({:foo "bar"} :psych "not-here")) | |
(= "not-here" (:psych {:foo "bar"} "not-here")) | |
(= "not-here" (get {:foo "bar"} :psych "not-here")) | |
;; <INCIDENTALLY> | |
;; Incidentally, using a default may not be what you want; you probably want to | |
;; use `or`: | |
(not= "not-here" (:psych {:psych nil} "not-here")) | |
(= "not-here" (or (:psych {:psych nil}) "not-here")) | |
;; </INCIDENTALLY> | |
;; </INCIDENTALLY> | |
;; There are two factors in choosing how to access items in a map: 1) the nature | |
;; of the map and/or key, and 2) the semantics of the map and key. | |
;; == NATURE OF THE MAP AND/OR KEY | |
;; Could the map be nil? You want to use either 'key as function' or '`get` as | |
;; function': | |
(let [m nil] | |
(m :foo)) | |
;; => NullPointerException ... | |
;; Could the key be anything other than a keyword? You want to use either 'map | |
;; as function' or '`get` as function': | |
(let [k nil] | |
(k {:foo "bar"})) | |
;; => NullPointerException ... | |
(let [k "foo"] | |
(k {:foo "bar"})) | |
;; => ClassCastException java.lang.String cannot be cast to clojure.lang.IFn ... | |
;; Could either the map or the key be nil? Could the key also be not-a-keyword? | |
;; Use '`get` as function'. | |
;; == SEMANTICS OF THE MAP AND KEY | |
;; Is the map a 'function'? For example, is it a transform: | |
(let [m {"ping" "pong"}] | |
(get m "ping")) | |
;; Realizing that it is a function and treating it semantically like a function | |
;; will help you later when you realize you want to do some more complicated | |
;; transform, or provide some default. In this case you should use 'map as | |
;; function': | |
(let [ping->pong {"ping" "pong"}] | |
(ping->pong "ping")) | |
;; Now you can make `ping->pong` into an arbitrary function. | |
;; Is the key a 'function'? For example, is it accessing data: | |
(let [k :height-in-cm] | |
(:height-in-cm {:height-in-cm 180.34})) | |
;; Realizing that accessing data and calculating data are equivalent (in a | |
;; purely functional sense) and treating the key as a function will help you | |
;; later. | |
(let [height-in-cm (fn [m] (* 2.54 (:height-in-inches m)))] | |
(height-in-cm {:height-in-inches 71})) | |
;; Now you can make `height-in-cm` into an arbitrary function. | |
;; This is function punning, and Clojure allows it in many ways. | |
;; == CONCLUSION | |
;; Which method of accessing a map you choose depends on whether the map and/or | |
;; key could be nil (in which case don't treat either as a function), and | |
;; whether semantically your code wants a function (in which case treat a map or | |
;; a keyword as a function, but make sure that your code could take an arbitrary | |
;; function just as easily). |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
find
is worth mentioning as an often-overlooked but very useful way to look up items in a map when you need to know whether or not there is indeed a key present, even if its value isnil
: