Clojure Tidbits | Pt. 1 The Stepup Rule
There are several new things that I‘ve recently learned about Clojure, but since they’re not really related to one another I’ve decided to spread them out over a series of short posts.
The Stepup Rule
In Clean Code: A Handbook of Agile Software Craftsmanship by Robert Martin, we learn about something called the stepdown rule, which tells us:
We want the code to read like a top-down narrative. We want every function to be followed by those at the next level of abstraction so that we can read the program, descending one level of abstraction at a time as we read down the list of functions.
Besides simply providing some order to a group of functions, organizing our code in this way has the added benefit of subtly conveying the intent of a function — the closer to the top a function is, the higher the level of its concerns. The inverse is also true. Although this should probably be clear from the names we give our functions, we can reinforce the statement of intent that is a function name by its placement relative to other functions.
In Clojure, though, we can’t invoke a function before it has been declared. More specifically, the name of the function needs to be defined, even if the function itself isn’t specified until later — we can accomplish with the following:
If we follow the pattern of organizing functions within a namespace based on their level of abstraction, with the highest at the top, we end up with something like this at the top of our namespace:
(declare space-free? is-integer? is-space-on-board? look-up-space generate-diagonal-coords is-there-a-win? not-empty? top-left-to-bottom-right-coords top-right-to-bottom-left-coords)
Yeah, that’s kind of a mess, and it was previously part of my tic tac toe game in Clojure. It’s a consequence of the fact that higher level functions will inevitably call lower level functions, which necessitates all these forward declarations.
If we invert the Stepdown Rule, though, and place our highest level abstractions at the bottom of the file and the lower level ones at the top, most or all of the forward declarations become unnecessary. The spirit of the Stepdown Rule remains intact, but the way we implement it has changed. So long, declare…
Tl;dr —We should organize our Clojure code with higher level functions below lower level functions. Uncle Bob agrees.