11/20/2019

Your code is too complex! - How to choose the right technical solution from complexity point of view?

Introduction
As software developers we all know that every problem has several solutions. It is always up to the situation which solution will be implemented. For example a student at an exam will implement most likely the fastest solution, since a programmer at a multinational company will choose a much more complex solution for the same problem, which is prepared for all possible corner cases. Let’s take a look at the possible solutions and figure out how to choose the optimal one.

Expectations from the code

There can be thousands of expectations against a piece of code, like: it should reuse the already implemented code, it should be reusable by other components, it should work properly in every corner cases and in every possible environment, it should be easy to understand the code, it should be easy to extend or modify the code, it should be testable, it should be fast, it shouldn’t use too much memory, it should follow the style of the programming language etc.
But all of these points are relative, like “it should be easy to modify”. What does easy mean? Easier than what? Or it should be reusable by other component. On which level? It’s full functionality? Each of its classes separately? Etc.
It can also be that some points are absolutely irrelevant. Like a student at the exam knows well that no one else will reuse or change that code anymore, so they really don’t care about all these points, that’s how they can reach quite good functionality in several hours. But the software usually have a lot of bugs, it can not handle the corner cases, it is running only in one dedicated environment etc. To reach the same functionality at a big company may take weeks or even months. But at big companies it is always the goal to reuse the already existing code and make your code reusable, make your code readably and adaptable etc.
If you really consider all these points and you really try to write a code which is fulfilling all of them you can find yourself in the situation that you are building the spaceship (something very very complex), even though it should do only something really easy.

The dark side of building a spaceship

To build a spaceship is really difficult. 99.99% of the humans is not able to build one. Luckily 99.999% of humans doesn’t need it is the same in case of software development.
Once I had a colleague. He was supposed to write a simple database layer, to store some data, nothing special. It could have been done by adding 3 SQL tables and adding maximum 5 simple functions which can add and remove data.This is something could be done in one hour. This guy disappeared at this point for three weeks and came back with a solution which was prepared for everything, really for everything. It has a lot of tables and index tables and a complex library over that to be able to use it. It was basically a small data handling system.
At the end none of its user was happy.
The very first problem: why did it take so long to implement it? Yes, time is a very important factor in software business. Don’t waste your time.
On the other hand its interface was far too complex, no one could figure it out how to use it, and just to add a new element took at least 5 lines. And I still didn’t mention the amount of bugs in that implementation.
At the end he thought he did something great, but everyone else had a different opinion.
This example shows why not to implement a solution which is prepared for everything, especially for situations which will never happen:
It will take a lot of time, it will be complicated to use it, people won’t understand what you are doing, higher complexity means a higher chance for bugs etc.
In a lot of cases, even if your code is technically reusable noone will reuse it, because it is too complicated to use and understand it, so it takes more time to reuse it, than doing your own implementation.

Finding the middle way

The right solution is always up to the task. The points mentioned as expectations are very often contradicting with each other: the faster code usually uses more memory, the code which is optimized for runtime and memory consumption is very often nor reusable, neither readable etc.
So you should think point by point what is relevant in your case: is it critical to optimize its runtime or memory consumption? Will anyone reuse your module in the future? Is it something for long term (so that it should be easy to maintain)? Will it be used in different environments? Figure out what are the real needs and implement your software in that way, nothing more, nothing less.
So the university student at the exam does it well: no one will ever work with his code in the future, so it makes no sense to think about points like readability, maintainability or reusability. Only the functionality is important and the given time frame is tight.
The software developer at the big multinational company does it also well: most likely his code will be used several years long, it will be touched by many developers, used by several users in different environments and there’s a high chance that it will be reused somewhere else (in other software or other software components. So it really makes sense to come up with a solution which fulfills all these non-functional requirements, even if it takes much more time, than implementing the raw functionality.

Always think about the situation of your software and come up with a solution based on that.

11/08/2019

An overview on different programming paradigms - Functional style of programming in modern languages

Introduction
Is there only one way to write computer programs? Of course not, you can implement the same algorithm in several ways or you can use several different programming languages.
Is the way you are thinking the same in same of every programming language? Not really, each language provides different opportunities which are making a difference in the way of your thinking. Sometimes these differences are pretty small, but sometimes they are really big.
Let’s take C and C++ as an example. They syntax is almost the same, in C++ you can do whatever you can do in C. But the style how most programmers are using C and C++ is slightly different. Since in C you can organise your code only by functions/methods, C++ is supporting classes and objects as well. And it makes it possible to build up your programs in a slightly different way, where your main blocks are classes. At the end of the day both programs are doing the same, but the way of thinking behind them is totally different.
C is a purely procedural programming language, since C++ is supporting multiple programming paradigms, like procedural and object oriented programming and it also gives you the opportunity to use some functional programming-like programming style.
Both procedural and object oriented programming are imperative programming paradigms. In case of imperative programming, you are telling your program step by step what it should do, like: create a variable, assign a value to that, print that value to the screen etc.
The way is not to tell the steps, just tell what you expect from your program, you are just describing its logic. It can be also done in multiple ways, you use the functional programming paradigm or the logic programming.
There are programming languages which are only supporting one dedicated programming paradigm, like Haskell is purely functional, or Prolog is supporting only logic programming, but there are also languages supporting multiple paradigms, like C++.

Imperative paradigms

Most programmers meet first with imperative paradigms. In case of imperative programming you are using statements, which are running in a specified order and these statements are changing the state of your program. Like changing the value of variables etc. Based on how these statements are organised we can talk about procedural or object oriented programming.

Procedural

In case of procedural programming we are defining procedures (subprograms) and then we are calling them in a specified order, with specific parameters. The subprograms can also call each other. Most well-known examples are C, Basic or Pascal. 

Object oriented

In case of object oriented programming the basic elements of your program are objects. Objects are encapsulating data and related algorithms. The similar objects can be organized into classes. Object oriented programming should support the following principles: encapsulation, abstraction, polymorphism and inheritance. To do proper object oriented programming you can follow principles like SOLID or use design patterns. Most popular object oriented languages are JAVA and C#.
I also have to mention that the programming paradigm is not only about the language your are using. You can also write programs in JAVA, which are not really object oriented (by writing all code into the same class) and you can also imitate object oriented programming in C.
So it is much more about the way how you are thinking while writing your program.

Declarative paradigms

The other concept is declarative programming. In this case you are not describing your program step by step, but you are describing it in a similar way to mathematical logic. In this case you have no direct control over the state of your program, so you can not change any parts of the state which an unrelated to your computation, which are out of the scope of your code. Such modifications of the state are called side effects and they are a source of a lot of bugs in case of imperative programming. In case of imperative programming you are avoiding side effects.
There are multiple style of declarative programming, just like functional programming, logic programming or database programming.

Logic programming

Logic programming is based on formal logic. The most popular logic programming languages are Prolog, ASP and Datalog.

This is a short example written in Prolog:

mother_child(trude, sally).

father_child(tom, sally).
father_child(tom, erica).
father_child(mike, tom).

sibling(X, Y)      :- parent_child(Z, X), parent_child(Z, Y).

parent_child(X, Y) :- father_child(X, Y).
parent_child(X, Y) :- mother_child(X, Y).


Functional programming

Functional programming has been introduced with the programming language Lisp in the late 50’s. Nowadays this paradigm is becoming more and more popular. It is supported by programming languages like Haskell, Erlang, F# or Clean. But more and more modern programming languages are supporting it by new language elements, like lambda functions. So it is always possible to write functional style programs in languages like Java, C#, C++ or Python.

Idea of functional programming

For the first look functional programming can be a bit strange for the ones who used to do only imperative programming before. Functional programming is based on lambda calculus. In functional programming you are using only functions without side effects. That means you can not modify the state of the memory directly, you can only calculate the value and return it. Then by using technologies like recursion, currying or higher order functions you can build up your whole programming from functions. For the first most likely it looks first. Let’s see some examples for some problems.

Functional programming examples

The below examples are written in Haskell.

N-factorial

This is a classical program which can be solved in both iterative and recursive way in case of imperative programming. In case of functional programming we will use of course the iterative way.
Code in haskell:

factorial n = if n < 2 then 1 else n * factorial (n-1)

Map list

Map is a function which is expecting a function and a list as parameters and it calls the function for each element of the list. It is a bit like using a cycle in case of imperative programming.
This is the implementation in haskell:

map f xs = if null xs then [] else f (head xs) : map f (tail xs)

So in case if an empty list it is returning an empty list, otherwise it is calling the function for the head, which is the first element of the list, and then calling himself on the tail, which is the list without head.

Contains function

As a last example: check if a list containing a dedicated item. It would look like this:


find _ [] = False

find x (x:xs) = True
find x (_:xs) = find x xs

So in case of empty list the answer is false. If the first element of the list is the one we are looking for it is returning true. Otherwise it is calling the same function for the list without it’s first element.

Functional programming in modern languages

Next to the purely functional languages most of the well-known, modern languages support some elements which are making it possible to write code in functional style. These are mostly the lambda expressions and passing functions as parameters.

C#

In C# Lambda expressions are supported. You can also pass functions as parameters by using delegates. The predefined Func template delegate makes it really easy. Using functional style of programming can be really useful by using LINQ or just at working with collections (foreach, filter etc.).
The below example is solving the factorial problem in C# using a functional way of programming by using the Func delegate and lambda expressions:
Func factorial = null;

factorial = x => x <= 1 ? 1 : x * factorial(x-1);

Python

In python it is pretty easy, since it is using weak typing and it is possible to pass functions as arguments just out of the box. Lambda functions are also supported.
This style of programming in Python can be especially useful while working with data processing.
The factorial example in python:

fact = lambda x: 1 if x == 0 else x * fact(x-1)

C++

Modern C++ also supports both lambda functions and passing functions as parameters (by using std::function), so I thought everything is given to write programs in a functional style. Then I went a bit deeper and I realised one thing which made it impossible: I couldn’t find a clear way to do a recursive call from a lambda function. There are some hacks and tricks which make it possible (like using template functions etc.), but none of the solutions are clear enough to use as a suggested way.
My other idea was to use the good old preprocessor directives to write functional-like programs in C++, but it there the issue was the same. There was no way to do a recursive call from the macro.

Database programming

An other declarative paradigm is database programming. A good example is the SQL. Most of programmers knows it, but never thought that it is a declarative language. You can describe what would you like to do with the data in the database, but you don’t define how to do it.

Summary


There are several different styles of programming which have an influence on the way you are thinking. Different programming languages support different paradigms, each paradigm has its own area, specialities, advantages and disadvantages.

How I prepared my first online course

Since long I didn't publish anything here. It's because I was busy with some other topics, but now it's time to share the result...