One is the Loneliest (But Best) Number

Malina Tran
Tech and the City
Published in
3 min readAug 12, 2016

If there is one lesson learned about functions, it is the rule of one. That is, a function should do one thing well and only that one thing. It’s tough to tell when a function is doing multiple things. A good rule of thumb is if you can extract another function from it (and have a good name for it), it is most likely that original function was doing too much. And by doing one thing, we’re assuring that there are no side effects (e.g. altering variables, invoking another function).

Lesson #2 about writing functions: statements within the function should all be at the same level of abstraction. A high level of abstraction would probably be something like `get-empty-cells` with no implementation details shared. On the other end of the spectrum, a low level of abstraction would handle more formatting (e.g. adding line breaks, etc.) than logic. It would probably look like this:

(clojure.string/join “ | “ (cons “\n” (concat (apply concat (interpose [“\n”] (partition size formatted-board))) “\n”)))))

That’s actually a real line of code I use to format my tic-tac-toe board, namely to add dividers and line breaks in between each function for readability! In other words, you can’t have various levels of abstraction housed with the same function. There needs to be the same level abstraction so that your function reads something like this (all interface and no implementation details here):

(defn mark-board-with-move 
[board players player]
(let [move (player/get-move player board players)]
(player/make-move player board move)))

Writing small functions comes hand-in-hand with choosing good names. The smaller a function is, the more focused, the easier it is to choose a descriptive name. Uncle Bob assures us that there is nothing wrong with a long name; if it’s descriptive, it’s preferable to a short enigmatic name. It’s also better than a long descriptive comment.

In cases where we use `switch` statements, there should be an interface. In cases where we use `if` statements, there should ideally be one line of code, preferably a function call. Error-handling? All abstracted into a single function. Yup, that level of abstraction is what we’re talking about.After reading the third chapter of Clean Code, focused on functions, I decided to take some of its lessons to heart.

This is a method that I had written to prompt the user for input, check its validity, and then do any error handling in the event the user enters an invalid input. Already, it sounds like it is doing too much.

Here is the refactored version of the code. I created a method for each one of those roles: `get-valid-size` checks if the input is valid, `run-size-loop` has a try-catch block, and `prompt-for-size` coordinates the prompting of the user.

Code should flow; writing a function should help craft a narrative and provide clarity to the purpose of the code. And each function plays a role in lending itself to the narrative. One of the things that I’m working on is breaking down unwieldy functions and being mindful of people reading code. Telling better stories, writing better code — that’s the goal.

--

--