Monday, October 16, 2017

Ideological Programming

Today I was following what is actually a very good tutorial on SDL2, in C (despite it being written for C++).  I came across an excessively complex section of code.  The tutorial has me writing an initialization function.  This is an excellent idea, as it groups a bunch of related actions together.  Unfortunately, it is both hard to read and hard to write, and as far as I can tell, it is purely because the writer subscribes to a particular programming ideology.

This function starts by initializing a return variable, used to indicate failure or success.  Then it initializes the video subsystem.  If it fails, it sets the return variable to false, otherwise it goes into a nested conditional, creating a window and then testing for success, then it goes into another nested conditional for starting up the image loading subsystem, and only then does it get a surface to draw to.  This is nested three levels deep.  Each level tests if some initialization worked, and if not, it prints an error message and sets the return variable to false.  The nested conditionals make the code hard to read and hard to write.  There is a reason the writer of the tutorial did this though.  It is an ideological reason.

There is this programming ideology that says a function should only have a single return point.  In other words, it should not return anywhere except at the end.  Programmers who subscribe to this tend to use return variables and nested conditionals to avoid returning in a conditional before the end.  What is the benefit of this ideology?  There is none.  It is based on the idea of mathematical functions.  Mathematical functions can only have a single return point, and some programmers believe that this should be applied to functions in programming.  The fact, however, is that the word "function" means very different things to mathematics and programming.  Programming functions are loosely based on mathematical functions, but they are not, by any stretch, the same.  There is one exception: In functional programming languages, functions are analogous to mathematical functions.  As such, pure functional languages don't allow multiple return points in functions (this may not always be obvious though).  C is not a functional language though.  There is no benefit to adding this artificial programming constraint.  In fact, in the worse case, it will result in inferior machine code.  While a well designed compiler might be able to optimize away most of the extra work for nested conditionals to get to the return point when a failure case occurs, it will never end up better than a set of conditionals where a failure just returns inside the conditional testing for failure, instead of using a return variable and nesting.  In many cases it will end up worse though.  Intermediate return points avoid unnecessary work, especially in a case like this.  More importantly though, the flattened version of the code is both more readable and easier to comment.  The ideology might sound good, but it is applying pure math principles to a more complex and flexible discipline, and in doing so it produces worse outcomes.

This is not the only programming ideology that is expensive and wasteful without producing any benefit.  Object orientation as a programming paradigm is founded on the ideology that everything should be an object.  Following this ideology results in hard to read code that uses significantly more memory than necessary and performs poorly on modern computing systems.  Programming to a particular ideology might have some academic value, but in real life situations, it generally just causes problems.

When writing a program, these should be the primary goals, starting with the most important:
  1. The project is completed within acceptable margins for cost and schedule.
  2. The program produces the correct output for every possible input.
  3. The program is cheap and easy to maintain.
You can add other goals after these, but most will be dependent on the particular project.  Aesthetics might be important for one client, while specific features may be important to another.  Notice, however, that none of these three include anything about following a particular programming paradigm or ideology.  Unless it is a critical requirement of the project (which is almost unheard of outside of academia), maintainability always trumps adherence to a particular paradigm.  Following a particular constraint for something that happens to have a similar name from a related field is not an excuse for writing code that is hard to read, hard to comment, and hard to maintain.  Sometimes performance or correctness is more important than maintainability, but few of these ideological programming constraints improve performance or correctness, and many times they actually hinder one or both of these.

Ideological programming can be fun as a hobby or a purely academic pursuit, but in the real world, following abstract rules that have no practical value is a waste of everyone's time and money.  Effective programming is about using your tools efficiently and effectively.  When you eliminate a tool from your toolbox for a purely ideological reason, you artificially limit your skill and the quality of your work.  Just say no to ideological programming.