At this point I have been programming for nearly a quarter century. I have done multiple languages and worked with multiple platforms and technologies. Some of it can become quite complex and with that complexity comes extensive testing and solutions that just don’t feel elegant at times. I often find myself asking the questions “How do I simplify this?” or “There has to be a simpler way, what is it?” With these questions I often lean on refreshing the basic principles of programming like classes, variables and most importantly functions. In this post I will cover some of my thoughts on functions and that if we can really isolate the idea of a function, and its parts, as this will give us half the solution to any problem thrown at us.
If you are a programmer that has been doing it for any length of time, you may be thinking about a whole slew of things when the topic of functions come up. Ideas like parameter types, in/out parameters, variable number of parameters, function naming, return types, function length etc. For the time being lets just distill a function down to its essence without all the “flowery” terms. A function takes zero or more values, does something with them and possibly returns a value. For example, an “add” function may take two values and return a sum value. If we are not considering optimization, memory management or the like wouldn’t you say that a basic function can do some pretty incredible things? With this basic idea of a function, what do we need to do to make sure that it can do its thing? Well, we need to make sure that the info we give it is solid and correct. We then just need to make sure that its result is correct based on those inputs. Sure it might do some bad things in the middle but if the inputs and outputs are good, we have a large part of the work done.
If you have any experience with functional programming, you may be saying to yourself “purity”. In a way this is what I am saying, but again lets remove the notion of purity for a minute and just look at the idea of a function as a stand-alone idea void of any complex labeling. With a function that has correct and valid inputs and a solid output all that is left for us as programmers is to make sure that it stays that way. However, if you want to dive into some of these ideas of purity, check out my blog post on Applying Functional Programming to OOP.
Ok we have our distilled-down idea of a function. It is a piece of code that could take some inputs and may or may not return an output. If we can make sure that what comes into the function and what leaves the function are always right, the function itself is basically correct. Now it may not be the most efficient or it may do a bunch of basic “bad things” but from a holistic overall approach it works.
So let’s think about the function inputs. Can they be simple? Can they be typed? What can we do to make sure that the inputs we provide the function is as solid as they can be so the function can be confident that it is being told the truth. Should we validate them before calling the function or should the function itself make sure they are right? Well, that is up to you, but just make sure they are right in either case. If our “add” function expects two integers, isn’t that easier to think about than some complex wrapper class around an integer value? A class that may be null or may be dirty etc.?
I am not saying we can always create functions that work on strictly primitives, but the closer we can get to that simple value, the easier things could become. Often times when I am working on complex systems, I purposely step back and say “Hey, can this just be simpler? Can I reduce the number of inputs? Can I make this a simple number instead of this complex ugly class instance? Sure the instance might be more efficient, but what if efficiency wasn’t a concern? Would I still do this?”.
I find that if I take a function and think of it as a basic block in isolation and give it the simplest types of inputs, and reduce the number of inputs, that the function gets really solid and error proof. I mean, how hard is it to mess up an “add” function that takes two integers and returns an integer sum? You could code that without even needing Google.
Like the inputs, I then think about the outputs. Again, very high level and without all the jargon that flies around in the development community. What is this function really suppose to tell me at the end of the day based on the inputs? Is it simply a sum? Is it going to return anything at all? Does the result have to be an array or can it just be a single primitive? Can you CLEARLY see the path between the inputs and its output when looking at this function in isolation? If you can’t, maybe the function is still too convoluted and needs to be broken down into other functions.
I think this is a good time to also mention that one line functions are NOT BAD! As long as the function simplifies the concepts, it works no matter if it is one line or 20. Of course we want to keep functions as short as possible, but one line is not too short is all I am saying.
You have probably heard of the age old adage of functions that they should do one thing and do it well. It is true and I think if you look at a function in a distilled-down form and think of its inputs and outputs only without the fancy terms. If you think like this, you will already be 90% of the way to having a function that does one thing and does it well. The other 10% is just to make sure that the function isn’t brining in other data (globals, databases, files etc.) and NOT counting those as inputs. Along with this idea we also need to make sure that the output is the same for the given inputs and we got it nailed down. Again, this is the idea of “purity” in functional programming but you don’t need to study up on functional programming to get this concept. In fact, I think functional programming takes this to extremes at times, but I digress.
Ok, so if you follow the idea of taking a function in isolation, simplify its inputs and outputs and making sure to avoid crazy global use or not accounting for info being brought into the function from other means, you should have solid functions. As this post is titled, that is only HALF the battle. What is the other half? Well, obviously if you are working in object oriented programming you have things like classes, structures, variables, enums and all the other stuff that goes along with programming. However, I think that if you can write programs that have well solidified functions you will get the most bang for your buck in any project you work on. At least half of it will be solved.
Until you have seen a several thousand line program get cut down to a couple hundred lines by a few solid basic functions, you may have never felt the joy that great functions bring. The elegance is mind boggling. If you want to master any single idea in programming, functions is where it is at. Master them and just watch how much value you get out of your work!
In this post I talked a bit about the ideas of functions, but more importantly, how you should think about functions from a very distilled idea of simplicity and elegance. By taking the idea of a function and making sure that what goes into a function and what comes out of it is pure and solid we can get some pretty amazing functions. Functions that can greatly simplify and drive even the largest projects. We all have written bad functions. We have written bad functions yesterday. Then today, taking a hard look at them, we can realize they are bad. But it is better to simplify the function down into its basic form, study that, determine what it depends on and what it provides can be invaluable. Once we see the function is solid, then we can go about adding additional syntax sugar or work on efficiency.
If you are ever having trouble reading your own code, distilled its functions down, refactor them, get at their most basic form and you will often find out what code is doing (right or wrong) and how to improve it. Thanks for reading! 🙂