True Story Follows
I was refactoring some code that was initially put in place as a prototype to build upon later, all of which was based on boolean logic. Imagine, if you will, code that went something like this:
The exact logic of the code in question has been abstracted away in this example, but close your eyes for a moment and imagine that the conditions themselves were much more in depth and complicated and were a series of conditional statements involving user behavior, page settings, and a mix of different data gathered and stored in our database.
In my opinion, one of the most important elements of coding is readability. This eases the process of debugging problems, collaborating with peers, and overall code maintenance, and in the above example there would be numerous qualms with how processing was being done.
When initially trying to ponder how to go about making the above code more “beautiful” and subsequently apply the positive side effects of such an effort, a voice arose from the back of my head of from one of my old college professors, Dr. Okasaki (author of Purely Functional Data Structures), leading me to define behavior in an entirely functional manner. Up front, the focal point of my solution was a function factory as seen below:
The above functions take others functions as their arguments and return a single function that can then later be evaluated. The result is that any number of functions can be combined and chained together into different manners to create a “Voltron” function, if you will (and I will).
Now, in this example I’ve extracted away the various conditions that were being evaluated to True or False. But what really went down in real life is that all of the conditions for the pieces of logic were abstracted away into their own functions in which the name of the behavior was defined as the function’s name. With the functions created and the function factories created, we can then create a dictionary like so:
In the above example, all of the variables used are functions.
As confusing as this may be initially, it was at least the most concise way I could think of to define the desired logic. Another advantage here is that the overall (complex) behavior is defined in a single location, and can be easily manipulated and reasonably understood. Several other advantages: the sub-behaviors themselves are defined in a single location in a very small number of lines of code and can easily be tested, validated, understood, re-used, and manipulated.
Finally, one disadvantage in the presented code’s state that is a major performance hindrance is that functions are potentially being called numerous times, and in the real world use of the code, calls to the database are being made in those functions. A quick solution is to create a memoization decorator (just google that one if you’re not familiar) specific to the needs of the listed functions. We now have matched or better performance to the initial logic, far more readable code, and code that can be tested in a much easier manner.
One last thing to point out in my thought process here was to consider the use of classes or the use of functions. Classes can be thought of as something that stores data whereas functions are used to store behavior (or data that stores behavior vs behavior that stores data, respectively). Passing functions to other functions can be extremely powerful tool, and is something that I’ve only recently learned about leveraging.
Of course, thus far, no one has affirmed my thinking that this was a really cool solution to the problem at hand. Others might think it was complete idiocy, but so far I don’t know.