Click here to Skip to main content
15,867,568 members
Articles / Programming Languages / C#

Imperative and Declarative Programming

Rate me:
Please Sign up or sign in to vote.
4.57/5 (23 votes)
17 May 2015CPOL5 min read 18.7K   9   2
Imperative and declarative programming

Out of curiosity and a wish to refresh my memory of some things I learnt in university, I recently started reading about imperative and declarative programming. Before long, I had entered a never-ending cycle of conflicting definitions, opinions and debates. It really is an interesting topic to explore, however there comes a point when you have to consider what is actually useful in your day-to-day work, assuming you are not an academic, but rather a programmer who wants to write good software.

My thoughts on this are as follows.

It is useful to understand the essential differences between imperative and declarative programming, along with the broad definitions of function purity and referential transparency. Why? Because these concepts invite you to open your mind to alternative ways of writing code. Too often, we get attached to a particular way of thinking and solving problems. The more models we have in our toolbox for viewing a software program or system, the more able we are to come up with effective solutions.

As important as understanding these concepts, is understanding some of their relative advantages and disadvantages. This helps us to know what is good and bad about the code we are writing. We do after all want to write good code, so understanding what “good” looks like is therefore obviously important.

Having said all of this, it is counter-productive to get bogged down in the detail and correctness of one definition over another. Keep it practical!

Imperative v Declarative Programming

Broadly speaking, imperative programming is telling the machine what to do, and declarative programming is telling the machine what you want.

A common example provided in C# is the use of a loop to filter a list, as opposed to a LINQ extension method.

So this is imperative:

C#
var myList = new List { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
var oddNumbers = new List();

foreach(var n in myList)
{
   if(n%2 != 0)
      oddNumbers.Add(n);
}

…and this is declarative:

C#
var oddNumbers = myList.Where( n => n % 2 != 0);

In the first example, we are telling the machine what to do:

“Take my list and go through it, and for each item in it if there is a remainder when it is divided by two, then add it to this new list.”

In the second example, we are just saying what we want, not how to get it:

“I want this new list to be a filtered version of my list, containing only items where there is a remainder when divided by 2.”

The key difference here is that in the second example, we are not telling the machine how to filter our list. Under the hood, it may loop through the items as we have specified in our imperative example, or it may prefer some other way of obtaining the same result. We don’t care.

Imperative programming is micro-management. Declarative programming is trust.

In English grammar, an imperative is a command or an order, and a declarative sentence is a statement which conveys information, so you can see where the terminology comes from.

Another way to look at imperative versus declarative programming is that imperative programming changes state, and indeed this is the definition offered by Wikipedia. However, this is where things start to get confusing, as in the above examples neither changes state more than the other, even though this is an accepted answer to the question on stackoverflow.

The answer to this problem stems from the fact that modern software programs tend to be a mixture of imperative and declarative programming.

In isolation, this is a perfectly declarative bit of code:

C#
myList.Where( n => n % 2 != 0);

…however to make it useful, we need to assign it to a variable, which means writing a command, telling the machine to assign our result to a particular location in memory, so in its entirety, this statement is imperative:

C#
var oddNumbers = myList.Where( n => n % 2 != 0);

Yet another way to look at things is that we can have a declarative function call, whose implementation is imperative, and thus we have various levels of abstraction in our programs, from assembly language to application programming.

A feature of pure declarative programming is that it displays referential transparency. This means that functions do not have any hidden side-effects, and that given a specific set of inputs, the outputs will always be the same. In other words, functions are pure.

So here are two more examples of imperative programming:

C#
public int SaveProduct(Product p)
{
    _logger.LogSave(p); 
    return _dataLayer.Save(p); // notice how both these statements 
                               // change state - there are "side effects".
}

 

C#
// Here, even though there are no side effects, 
// we can provide the same inputs at different times and get different outputs.
public string GetFileContents(string filePath)
{
   return File.ReadAllText(@"C:\Users\Public\TestFolder\WriteText.txt");
}

 

And here is a “pure function”. No side effects, and given any input, the output will always be the same:

C#
public double Square(double n)
{
   return n*n; // Mathematical functions are generally pure
}

HTML is an example of an entirely declarative language – you describe what you want to see, not how to display it.

As an aside, it is important to recognize that imperative and declarative programming are programming paradigms, and that discussion of programming languages being imperative or declarative can get confusing, as many so-called imperative programming languages can be used in a declarative way and vice-versa.

Which is Best?

Pure functions and referential transparency are good things in that they produce predictable, testable and loosely coupled code. The movement against global variables is testament to the advantages of referential transparency. The modification of global variables represents a side-effect, a change of state, and accessing a global variable represents an unknown input. Dependency injection is also an example of a move towards “purity”. A class becomes more like a pure function when its dependencies are injected into its constructor rather like function parameters, and again this leads to more loosely coupled, predictable and testable code.

So why have imperative programming at all? Why not construct our entire systems around pure, referentially transparent modules?

The difficulty is that our brains like to think in terms of step-by-step processes, and so we find imperative style programs easier to write and easier to read. What results with imperative programs is code which is more likely to contain bugs, but easier to understand for humans.

Purity is a nice ideal, but in reality it is difficult to achieve.

The post Imperative and Declarative Programming appeared first on The Proactive Programmer.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer
United Kingdom United Kingdom
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralMy vote of 5 Pin
Sachin Mahandule22-May-15 2:29
professionalSachin Mahandule22-May-15 2:29 
QuestionThanks for the thought-provoking article Pin
Member 997646419-May-15 8:30
Member 997646419-May-15 8:30 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.