23 August 2021

Clojure Maps as Functions

Just a little Monday morning 'ah-hah!'

As stated in "Learn Clojure - Hashed Collections":

There are several ways to look up a value in a map...When the map in question is being treated as a constant lookup table, its common to invoke the map itself, treating it as a function...

I've known this for a while, but an implication of this property surfaced for me today. Suppose we have a map:

(def characters
  {nil " "
   :O  "O"
   :X  "X"})

The purpose of this map is to allow us to represent a tic-tac-toe grid as a string. Internally, we are using nil, :X, and :O to represent board state and need to 'map' those values to corresponding strings. Here's how I started:

(def characters
  {nil " "
   :O  "O"
   :X  "X"})

(->> grid                      ; [:X nil :O :X :X nil :O :O nil]
     (map #(get characters %)) ; ["X" " " "O" "X" "X" " " "O" "O" " "]
     (apply str))              ; "X OXX OO "

Summary: Start with a grid, map each value in the grid to its corresponding character using an anonymouse function that calls (get characters %) and combine them into a single string.

Then it hit me: the map itself is the function!

(->> grid
     (map characters)
     (apply str))

So, start with a grid, map it over the characters (using the map as the lookup function!), and combine the result into a single string. No need to define an anonymous function!

Since this was the only usage of the defined characters map I decided it could be inlined:

(->> grid
     (map {nil " ", :O "O" :X "X"})
     (apply str))

Start w/ the grid, map nil to a space, :O to the "O" character, :X to the "X" character and make a single string.

Voila! I'm probably just scratching the surface with this realization. It's these kind of straight-forward approaches that make programming in Clojure such a joy!