Skip to content

Instantly share code, notes, and snippets.

@pjstadig
Created March 6, 2017 16:28
Show Gist options
  • Select an option

  • Save pjstadig/25c8a775f5403a2f0e3ad94f81ef58ff to your computer and use it in GitHub Desktop.

Select an option

Save pjstadig/25c8a775f5403a2f0e3ad94f81ef58ff to your computer and use it in GitHub Desktop.

Revisions

  1. pjstadig created this gist Mar 6, 2017.
    93 changes: 93 additions & 0 deletions core.clj
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,93 @@
    (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).