Over the course of the past week, I had a chance to write some brand new PHP code and I took the opportunity to try writing in a (mostly) purely functional style. The experience turned out to be very pleasant and definitely helped us to accomplish our goals as we figured out the details and changes the project needed.
A different approach
- Instead of fearing the overhead in PHP for function calls, I tried to split everything out into separate functions: one responsibility per function.
- Operations were performed immutably, though this didn’t have a big impact on the code. Practically speaking, this meant a preference for `map`, `reduce`, and `filter` operations which also eliminate off-by-one and null reference errors.
- As I wrote the functions, I focussed on composability and modularity so that I could move the pieces around without making big changes elsewhere.
- Unlike my normal code, these programs ended up with a large flat array of functions. I normally prefer to have fewer functions and more order, but this level of unitized functionality ended up being quite the boon, making changes to program behavior effortless. In the few cases we needed to modify what the program did or fix a design bug, it was usually around a couple of lines of changes, mostly moving function calls into different orders.
- There are relatively few conditionals and very few variables in the code. For the most part, there are only variables where PHP forces me to use them to accomplish my goal, such as in obtaining the output from calling a process with `exec()`. Breaking down the functions has this effect. First of all, because the functions are often the callbacks for mapping operations, there’s no need to check if the passed-in arguments exist, which I would normally have to do if they were passed an array instead. Secondly, because the functions actually perform such a specific task, there’s little need to store the intermediate state within.
- There are zero errors from PHP Mess Detector and PHP Code Sniffer. The entire file has a Cycolmatic Complexity of 9 for 186 lines and a maintainability index of 67. I think it would be higher if it were commented more, but the code itself is fairly well self-documenting. Update: By adding PHPDoc comments, I was able to boost the maintainability index from 67 to 83, proving that there’s always a way to game the system. If you examine the code, you can judge for yourself how necessary they are.
Like I mentioned above, modifying this (and another) program was incredibly easy. Taking the time up-front to define the interface certainly helped me to be able to make this do almost exactly what it needed to the first time I had it “finished.” Because the functions were so small, it was simple to gauge whether or not they would work as expected, then it was just a matter of stringing them together. Like a well-tested project, this one made it a breeze to find the spot of the one bug we found, preventing me from spending so much time hunting it down. The functional approach definitely lends itself well to building reasonable, effective, and comprehensive tests. It was so easy working with it at times that it kind of made me all tingly inside.