In Which I Realize that Naming is Even Harder
I was just lying in bed reading when I had a small realization related to my last post, No, Seriously, It’s Naming1: my names are wrong–but maybe not how you’d expect.
If you recall, I left things like this:
(defn anagrams-for [word candidates]
(let [normalized-word (sort word)]
(filter #(and (= (sort %) normalized-word)
(not= % word))
candidates)))
A basic Clojure function to find anagrams, invocable like so:
> (anagrams-for "army" ["army" "mary" "john"])
("mary")
…but I started thinking, what is the interface? I mean, String
provides a bunch of capabilities we don’t really need, so what do we
really need? …and then it hit me:
> (anagrams-for [1 2 3] [[1 2 3] [3 2 1] [1]])
([3 2 1])
Because all we really need are some sortable-sequences. And what is a
String
but a sequence of characters?
In other words, we actually solved problems we didn’t even set out to
solve. Useful ones even! So the names of the variables (e.g. word
)
may actually be too specific.
Fun fact: it can even work on an infinite sequence of candidates2:
> (let [inf (cycle [[3 2 1] [] [2 1 3]])]
> (take 5 (anagrams-for [1 2 3] inf)))
([3 2 1] [2 1 3] [3 2 1] [2 1 3] [3 2 1])
…which may seem completely bonkers, except there are some concievable situations (time-bounded search strategies, property-based testing, etc.) where we might want to use something like this.
Anyway, I’m not going to beat you over the head with what this “all means” (you can draw your own conclusions), but do understand that this serendipity is not pure accident, but rather a consequence of the Clojure design philosophy.
And here’s Alan J. Perlis with the appeal to authority:
It is better to have 100 functions operate on one data structure than to have 10 functions operate on 10 data structures.