What is the difference between declarative and imperative paradigm in programming?

Viewed 310006

I have been searching the web looking for a definition for declarative and imperative programming that would shed some light for me. However, the language used at some of the resources that I have found is daunting - for instance at Wikipedia. Does anyone have a real-world example that they could show me that might bring some perspective to this subject (perhaps in C#)?

20 Answers

Imperative programming - you write the code that does the work

Declarative programming - someone else writes the code that does the work

I found it easier to distinguish between declarative and imperative based upon idempotent and commutative. Use the references to know about them.

Checkout this simplified version to know about idempotent.

Then I bring in the definition of "WHAT" & "HOW" to understand what "WHAT" & "HOW" actually mean. In declarative, you connect one data with another by defining a relationship between them. You don't mention how that relationship should be achieved rather "WHAT" that relationship is. Through a relationship you describe "WHAT" your output data looks like rather than "HOW" to achieve this output data.

Start drawing some diagrams in our head, draw some dots (data) and connect them with lines (relationship). Draw in all possible ways one to many, many to one & one to one. Give arrows to these lines, like this <-----------. All arrows should be facing left because all datas that a particular data is based upon must be calculated first and then move left to calculate that particular data.

If data a is based upon data b, data c and data d which in turn might be based upon on some other datas. Then b, c and d should be calculated first and only then a will be calculated. So a is on the left side of line and all others on the right. There will be 3 lines reaching a one from each of b, c and d.

This diagram has some properties:

  • NO data will violate the relationship it has with all other data
  • control flow or the order doesn't matter, of course b, c and d should be calculated before a but there is no preference between b, c and d i.e. it doesn't matter which one of these 3 is calculated first (commutative)
  • a is only based upon b, c and d and no one else. Hence, it doesn't matter how many times the relationship operation that calculates a using b, c and d is executed, same a should be achieved (idempotent). a is the end result of the relationship operation here. Basically, everyone who is affecting a should have a line pointing to a.

These relationships (lines) are like functions (functions of Mathematics and NOT programming). No doubt functional programming is famous among people of academia. Pure functions (of our programming, therefore not in bold) are like functions (of Maths, therefore in bold).

By now declarative might have started to sound like PURE and IMMUTABLE (which are generally used in Functional Programming) to you, if yes GOOD and if no GREAT. Because that's not the aim here, that's something that automatically emerged out of this pattern.

If your piece of code can be converted into this diagram then it's completely declarative otherwise, it lies somewhere else on the scale.

Declarative is close to Maths.

Now let's zoom in into these relationships (lines), to see what's going on inside the computer during program execution.

Imperative comes in. Here's where that ground-level work is done. In imperative, you mention step by step "HOW" it needs to be done and you know that this sequence of steps will create the requested relationship between one data (inputs b c d) and another data (output a). Here you create variables, mutate them, loop through array and all other things.

Imperative is close to Programming.

Instead of saying a program to be declarative or imperative, I prefer to see it on a scale where on the leftmost side I have completely declarative and on the rightmost side it's completely imperative. Remember, declarative is built on top of imperative, hence any declarative thing you see is actually imperative underneath. Generally, programs are a mixture of declarative and imperative.

Now, let's take these 2 examples:

Second example can be converted to the diagram like this:

      reduce_r       map_r       filter_r
a <--------- b <--------- c <--------- d

  • filter_r (relationship): c is only even numbers of d
  • map_r (relationship): b is all numbers multiplied by 10 of c
  • reduce_r (relationship): a is all numbers added of b

this should look like compound function of maths: reduce_r( map_r( filter_r( d ) ) )

In declarative, the job of developer is to break the ultimate goal (a) into subgoals (b, c) which will help to reach the ultimate goal.

of course under the hood of procedures map, reduce and filter is imperative code running.

Food for thought: if you are required to make an assumption on map function to go from left to right to make your code work as expected, you are actually doing imperative in the name of declarative.

References: purpleidea (James), www.dataops.live, wiki.c2.com

There were already lots of code examples added, so I'll not add another one.
Instead, I'll try to explain the difference between the two approaches in a way that I think makes their essence clearer than most of the definitions floating around:

  • A declarative approach focuses on the purpose of a particular algorithm, which often hides the algorithm itself.

  • An imperative approach focuses on the algorithm for a particular purpose, which often hides the purpose itself.

Just a practical example, of why CSS being declarative and JavaScript being imperative.

Imagine we have this navbar, and the user is currently looking at "Explore" option so it is marked as currently selected.

enter image description here

<ul>
  <li class="selected">
    <p>Explore</p>
  </li>
  <li>
    <p>Suggestions</p>
  </li>
</ul>

We want the title of the currently selected option to be blue, how do we achieve this with CSS and JavaScript?

CSS

li.selected > p {
  color: blue;
}

Here li.selected > p declares the pattern of element to which we want the property color: blue; to be applied. The result is "Explore" is highlighted in blue but "Suggestions" is not. Notice, our code describes what we want to happen and not how. How does CSS selector engine find "Explore"? We don't know and usually don't care.

JavaScript

let liElements = document.getElementsByTagName("li")
for (let i = 0; i < liElements.length; i++) {
  if (liElements[i].className === "selected") {
    let children = liElements[i].childNodes
    for (let j = 0; j < children. length; j++) {
      let child = children[j]
      if (child.nodeType === Node.ELEMENT_NODE && child.tagName === "P") {
        child.setAttribute("style", "color: blue")
      }
    } 
  }
}

This code is much longer and harder to understand. Other than that, it applies blue color to a selected option but never unapply it when selected class is removed. The blue colors are only reset when the page is reloaded. Notice, that with this code we specify exactly what needs to be done and how, step by step.

Conclusion

Each programming paradigm brings its advantages to the table.

CSS (declarative)

  • concise
  • we, as programmers, don't control how the CSS core does what we need. This gives the CSS core developers an opportunity to change the CSS selector implementation at any time. Why would the CSS core need to be changed? Perhaps, the CSS developers found a faster way to apply properties.

JavaScript (imperative)

  • customization. We control all aspects of how our code accomplishes the goal.
  • good for solving wide variety of problems
Related