Programming With Immutable, Funtional Objects: Eliminating Conditionals

Parallelism, concurrency and multicore hardware is (or must be) in the minds of all .NET developers. Certain techniques must therefore be learned and used to avoid corruption of data and raise conditions when dealing with multiple threads or tasks. One of these techniques is eliminating if/else conditionals by writing them in a generic manner.

Let's have a look at the following C# code:

  1. // We will assume we're in some static class.  
  2. public static bool If(Predicate<t> condition)  
  3. {  
  4.    if (condition(t))  
  5.    {  
  6.       return true;  
  7.    }  

By now you're asking yourself, "Isn't it silly to wrap an if statement in a method?" Perhaps, but by doing so, we allow the conditional parts of an algorithm to be executed concurrently or in parallel. This is important if we're paying for and/or using cloud-based services. Assume we are paying for a service that allows us to have 30 cores at our disposal and our algorithms are using maybe 10 of those cores we're paying for. In this case, our algorithms are causing us to waste money, since the other 20 cores we are not using.

Furthermore, static (as in not changing, that is) code must be modified every time we desire to add another test case to our already complex system. Nobody likes to look at a mess surrounded by parentheses that read something like:
  1. if ( (first_condition || second_condition) && (third_condition && fourth_condition)) 
What about the kind of code where conditionals are nested inside loops, or vice-versa? Our code becomes less readable and the placeholders that have white space could have been used by another valuable, such as the ones that make up a method name. Nested conditionals makes it nearly impossible for algorithms to execute in parallel. The majority of algorithms are complex and they are perhaps the most important, and they are the ones containing nested loops and conditionals. By creating methods such as the one shown above, we allow more genericity and composability of and for threads and tasks.

On the next article, we'll explore how to further cause the code we're writing to exploit the hardware available to us. Until then, let's work on replacing.