Simplifications

It's amazing how often we assume that things should be more complicated than needed.

September 10, 2021

I made a silly assumption the first time I implemented John Conway's "Game of Life" in clojure. Here's what my high-level evolve function looked like:

(defn evolve [grid]
  (let [neighbors (mapcat neighbors-of grid)
        in-play   (set/union grid neighbors)
        updated   (map #(update-cell % grid) in-play)]
    (set (remove nil? updated))))

It passed all tests and didn't look too bad. At 198 characters I was actually quite proud of it. Here's the version I came up with today:

(defn evolve [grid]
  (->> (mapcat neighbors-of grid)
       (map #(update-cell % grid))
       (remove nil?) set))

This version is just over half as many characters as the last one, partly because I dispensed with the let statment in favor of a threading macro. But it's also one step shorter--the set/union call is absent now, because it's actually not needed. When I first removed (and saw that the tests were still passing!) it I wondered if my test suite was incomplete. But after a bit of thought (and seeing the GUI still work flawlessly) I became convined that it simply wasn't needed.

Maybe this is a good example of what Oliver Wendell Holmes was talking about when he coined the phrase "simplicity on the other side of complexity".

-Michael Whatcott