Again, let’s say we need a cup of coffee. But, this time we just walk into the nearest coffee shop and say:

One coffee with 1 teaspoon of sugar and milk, please!

And there’s your coffee - no fuss over anything. You just declare the desired outcome and let the baristas take care of it.

The main benefit of this approach is a very high level of abstraction. Practically all the details we were so careful about in imperative programming are of no consequence anymore.

Benefits:

  • There is no dependence on mutable state. Notice how we never even mention the cup - the coffee shop will take care of it and all its intermediate states. We only get the coffee cup when it’s done.
  • Without the mutable state, we no longer care about the order of statements in our program. Is it one with milk and sugar and one without milk or sugar, or is it the other way round? It does not matter!
  • We don’t care about the global state either - we cannot spend all the milk in the coffee shop and do not depend on its availability. The shop takes care of that in a way which is transparent to us. Our own kitchens don’t work that way.

But it can’t be all nice and cool, there have to be some drawbacks, too. Otherwise, we’d never do any imperative programming at all.

Drawbacks:

  • We need a coffee shop to be able to get away with this, or at the very least someone willing to prepare the coffee. Try saying One coffee with 1 teaspoon of sugar and milk, please! out loud in your kitchen. If you do, you’ll just look ridiculous and most importantly won’t be getting any coffee. Also, whoever gets to actually make the coffee will do so by following an imperative program.
  • We are constrained by the variety of coffee shop’s offer. There may be a selection of several kinds of coffee and a few different sizes, but that’s it. Want coconut milk instead of the regular one? At home, you can add it yourself - in a cafe it’s possible only if they have it on their menu.
  • We can’t affect the performance in any way. There is a good chance that the baristas will be faster than you, but if they are not, you can’t do anything about it. Simply, you don’t get to tinker with the imperative program they use - it’s completely hidden away from you.
  • Different cafés all serve coffee, but in every one of them the flavor, quantity and the general experience are slightly different.
  • 5$ for a cup!

Now that we went through what declarative programming is, let’s see some real world uses. The most commonly mentioned example is HyperText Markup Language, aka HTML.

<html>
  <head>
    <title>A cool page</title>
  </head>
  <body>
     <div id="main">
        <p>Hello bright and sunny world!</p>
     </div>
  </body>
</html>

Here we have one HTML snippet that defines a very simple web page. All it contains is a title and one paragraph. These two texts are enclosed within a structure marked with HTML tags. That’s all there is to HTML - we only write the content and declare what’s what.

If we want to affect the style, we’ll use another declarative language Cascading Style Sheets or CSS.


#main{
  width: 70%;
  margin-left: 15%;
  margin-right: 15%;
}

p{
  color: blue;
}

In this example, we say that width of the element with id main is 70% of its parent width. Parent is the element within which our element is enclosed, so in this case it is page body. We also said that the remaining 30% of the whole page width will be evenly distributed between the left and right margin, i.e. 15% each. Then, we also declared that text in all paragraph elements (denoted by p tags) will be blue.

Again, the difference is that in an imperative program we would instruct the computer how to draw the page and format it. With HTML and CSS we only specify the desired outcome and wait for the results. Just like in the coffee example, for this to work we need to have software that’s going to actually do all the heavy lifting and draw the page - a web browser.

Another commonly cited declarative language is SQL. Let’s see one example query:

SELECT name FROM countries 
WHERE population > 10000000 AND continent = 'Europe';

We just say that we want the names of all the countries in Europe with population above 10 million. Do we loop through all the records in the database and check the continent and population one by one? Do we use a database index to speed up the search? Is the underlying structure of the index a B-tree or something else? Do we check the population first and then continent or the other way round? Short answer to all the questions above is - we don’t care! All those issues and many more are relatively transparently handled by databases’ very own coffee shops - RDMS (Relational Database Management Systems).

You must have noticed that all the major examples of declarative programming are focused on rather narrow domains. HTML, CSS and SQL are all what we call domain specific languages or DSLs. Not all DSLs are declarative, although most widely known declarative languages are domain specific.

So, how do we build our own declarative language?

One traditional way is to define a syntax to express the desired outcome, then use imperative programming to build a program to interpret the input and produce results. That’s exactly how web pages are processed - there is a well-defined syntax (HTML and CSS) and there are programs developed using imperative style of development that process it, i.e. browsers.

Another common way is to define a syntax, then build an engine that transforms the input in that syntax into a sequence of instructions to be executed, i.e. an imperative program. This is more or less how database management systems work. You give them your SQL, they transform it into something called execution plan, then the plan gets executed and you get results. We can even choose to only get the plan without executing it - to see this in action just prepend EXPLAIN to your query and run it in your database system of choice.

But can we do better than that? Can we have the goodness and ease of mind that declarative programming offers in general purpose software development or are we condemned to the perils of imperative programming as soon as we venture beyond the well-defined borders of specific domains?

These are the questions we will attempt to answer with functional programming!