15 October 2021

Clojure Reader Dispatch Overload

Also, how to return a collection literal from an anonymous function literal.

Call me crazy but the Clojure reader's "dispatch" macro (#) might be a bit overloaded.

Without going into too much detail, this macro enables the definition of:

  1. Set literals (#{1 2 3})
  2. Regex Patterns (#"\d+")
  3. Var-quoting (#'x)
  4. Anonymous function literal (#(...))
  5. Ignore next form (#_)

Seems a bit sloppy...

Why the rant? Well, I was trying to return a collection literal from an anonymous function:

(map #({% 1}) c)

...but was getting the following, somewhat unhelpful, error:

user=> (map #({% 1}) [:a :b :c])
Error printing return value (ArityException)...
Wrong number of args (0) passed to: clojure.lang.PersistentArrayMap

It turns out that you can't return a collection literal of any kind from an anonymous function literal. So this also fails:

(user=> (map #([%]) [:a :b :c])
Error printing return value (ArityException)...
Wrong number of args (0) passed to: clojure.lang.PersistentVector

As does this:

(user=> (map #(#{%}) [:a :b :c])
Error printing return value (ArityException)...
Wrong number of args (0) passed to: clojure.lang.PersistentHashSet

Due to the limitations of the reader macro you either have use the fn form:

user=> (map (fn [i] {i 1}) [:a :b :c])
({:a 1} {:b 1} {:c 1})

...or, slip in a do form:

user=> (map #(do {% 1}) [:a :b :c])
({:a 1} {:b 1} {:c 1})

:(

Maybe this isn't a direct result of the dispatch reader macro being overloaded, it's just a limitation baked into the way that part of the macro is implemented. Whatever the reason, at least you now know how to return collection literals from an anonymous function.

Idea!

I just realized that an even better approach in cases like this one, where your 'mapping' function returns a literal, would be to use a list comprehension!

user=> (for [i [:a :b :c]] {i 1})
({:a 1} {:b 1} {:c 1})

:)

You might be wondering why I want to convert a collection to a map where all the items from the collection map to the value 1. Well, stay tuned for the [exciting] conclusion in the next post...