4

When reading Clojure code I see 2 different patterns for how to structure maps:

;; 1.
(def event {:type :created :data {:start 1}})
;; 2.
(def event {:event/type :created :data/start 1})

And there is even support since Clojure 1.9 for Map namespace syntax making alternative 2 very simple to work with.

My question is what is the reasoning behind each usage (pro/cons) and is there any talk/article which explain the deeper ideas behind how to structure maps in Clojure?

2
  • 1
    The second one isn't a nested map at all; it's a flat, single-layer map with namespaced keys. That's unlike the link you pointed to, which actually is a nested map, with namespaced keys for the inner nesting layer. Commented Jul 27, 2019 at 2:15
  • 1
    ...anyhow, each of these has distinct and valuable use cases; I wouldn't say that either one is idiomatic in a way the other isn't. (For example: Actual nesting lets you make a change in one part of the tree while unmodified leaves retain their original identity; that can be a substantial performance optimization if you're propagating/recalculating based on updates!) Commented Jul 27, 2019 at 2:17

1 Answer 1

3

You seem to be confusing nested maps (literally, a map as a value of a key) with namespaced keys, which are still just keywords, but with some magic powers. A map of namespaced keys to values is still just a single level map.

You may want to look at the official namespaced keyword destructuring example for a bit of insight into one of the ways they are commonly used - the clue is all in the name, as in they seem to be designed to group keys into semantically relevant groups, whether that corresponds to an actual namespace in your code, or is something completely ephemeral that you're inventing on the fly, up to you.

A really neat property of them is the ability to alias namespaced keywords - so, if you require a namespace, then your local alias can refer to the same keyword - ie:

(ns foo.bar
  (:require [foo.baz :as baz]))

(prn ::baz/yours) ; equivalent to :foo.baz/yours
(prn ::mine) ; equivalent to :foo.bar/mine

(defn formatted-name [{:baz/keys [yours]}]
  (str "hi:" yours))

(prn (formatted-name {::baz/yours "clojurian!"}))
(prn (formatted-name {:foo.bar/yours "also"})) ; same arguments, but previous isn't hardcoded.

This is definitely something you see a lot, especially in larger codebases, and helps a ton with organisation.

Sign up to request clarification or add additional context in comments.

2 Comments

If I'm following correctly, ::baz/yours doesn't reference anything in the foo.baz namespace, right? If so, is something like ::mine largely used to avoid collisions in maps that might bounce around several namespaces?
Yep! They are just baked values, it’s not a reference to anything except themselves. I know that seems obscure, but if you squint a bit, they’re like string constants that have special powers.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.