A map constructor for Clojure/Script which emulates JavaScript’s ES6 object creation shorthand by introducing shorthand notation.
Add us.chouser/spread
as a project dependency:
{us.chouser/spread {:mvn/version "0.0.1"}}
(require '[us.chouser.spread :refer [k.]])
(k. :a "alpha")
;;=> {:a "alpha"}
(let [well-named-thing "beta"]
(k. well-named-thing, :c "charlie"))
;;=> {:well-named-thing "beta" :c "charlie"}
(let [well-named-thing "beta"
m {:x 7, :y 9}]
(k. well-named-thing, ~@m))
;;=> {:well-named-thing "beta" :x 7 :y 9}
Mix and match keyword/value pairs, bare symbols, and ellipsis (spread) prefixes to construct a map. Similar to property shorthand and spread syntax in JavaScript Object initializers
The k.
macro (a.k.a. keys.
) works well for constructing
maps that will be destructured with {:keys []}
. Also available are strs.
and
syms.
macros for {:strs []}
and {:syms []}
destructuring respectively.
See test/us/chouser/spread_test.cljc for all usages.
I don’t want to be penalized for choosing long symbol names when it comes to putting them into a map. Descriptive symbol/key names are desirable and a shorthand constructor makes them more practical.
ES6 introduced object initialization shorthand notation to JavaScript, which has been widely adopted and accepted as a concise and clear way to create data literals. Allowing for symbols to represent pairs is syntactic sugar that saves repetition at the cost of extra syntax. The evidence that this is reasonable and desirable is that the syntax was quickly adopted by the JavaScript community.
The spread notation was selected to match Clojure’s exising ~@
syntax for sequences.
Unfortunately ~@
does not work inside maps because the Clojure reader enforces an even number of forms inside a map literal,
without considering the effects of unsplicing:
`{0 1 ~@(range 2 4)} => Exception
Improving the Clojure reader to handle this case would appear to be a reasonable thing to do.
It is possible to trick the reader by providing 2 ~@
forms:
`{0 1 ~@(range 2 4) ~@nil}
=> {0 1, 2 3}
Alternative notations considered were …
and &
which are both interesting.
&
is symmetrical with destructuring.
…
is more common outside of Clojure.
Both introduce a minor ambiguity where …m
is the same as … m
(or &m
is the same as & m
).
~@
Does not have this ambiguity because it always means (unquote-splicing <expr>)
whether used with a symbol or any other expression.
Given some pre-existing bindings…
(def a "alpha")
(def b "beta")
(def m {:foo :bar})
Gerardus Mercator was an influential cartographer who also liked making maps. I’m pretty sure he would approve of a map construction macro. So you should too.