Declarative Programming Style


Application Security Testing: An Integral Part of DevOps

This article is based on Real World Functional Programming. It is being reproduced here by permission from Manning Publications. Visit the book's page for more information.
by Tomas Petricek

Declarative programming style

In this article, I'll talk about two properties of functional languages that together define the overall style of writing functional code. Most of the examples will be implemented in C# using the new C# 3.0 functional features, however you'll also see an example in F#. In many functional languages every language construct is an expression. The languages try to minimize the number of built-in concepts and are very succinct and extensible. We'll see that basic building blocks implemented in a functional language use recursion. However, writing every operation explicitly using recursion would be difficult, so we'll look how to write a single function that can be used in many variations for different purposes. These two concepts together define what a declarative programming style is, so after discussing them, we'll talk about the benefits of this style.

Functional programming and recursion

Functional programs try to minimze the use of mutable data structures and variables, which means that most of the programs are written without real variables and instead use immutable values. Lets start by discussing how to write a more complicated functions just using immutable values. For an example, we'll use the following function, which sums numbers in a specified range. This could be of course calculated directly, but well use it as an example of a calculation which uses a loop and we'll also see later how to change this code into a more generally useful function:

int SumNumbers(int from, int to) {
    int res = 0;
        res = res + i;
    return res;

In this case, we just cant directly rewrite the code not to mutate the value of res, because we need to modify the value during every evaluation of the loop. The program needs to keep certain state and the state is changed during every execution of the loop, so we cant declare a new variable for every change of the state as we did in our earlier example. This means that we need to do a fundamental change in the code and use a technique called recursion instead of using loops:

int SumNumbers(int from, int to) {
    if (from > to) return 0;
    int sumRest = SumNumbers(from + 1, to);
    return from + sumRest;

As you probably already know, recursion means that a function (SumNumbers in our case) calls itself (in our case this is when we calculate a value for sumRest variable). In this code, were using only value binding, so it is using only purely functional features. The state of the computation, which was originally stored in a mutable variable, is now expressed using recursion. When I originally mentioned that we cant declare a new variable for every change of the state I was actually wrong, because this is what our new recursive implementation does. Every time the function recursively calls itself, it skips a first number and calculates a sum of the remaining numbers. This result is bound to a variable sumRest, which exists as a new variable during every execution of the recursive function, so this is how the state is represented in our new implementation.

Of course, writing the recursive part of computation every time would be difficult, so functional languages provide a way for "hiding" the difficult recursive part and expressing most of the problems without explicitly using recursion.

Functions as values

The question that motivates this section is: "How can we separate the functionality that will vary during every use from a recursive part of the code?" The answer is simple - we will write the recursive part as a function with parameters and these parameters will specify what the "unique operation" that the function should perform is.

Let me demonstrate the idea on a previous SumNumbers function. We wrote a function that takes an initial value, loops through a specified numeric range and calculates a new "state" of the calculation during every iteration using the previous state and the current number from the range. In the function above we used zero as an initial value and we used addition as an operation that is used to fold the numbers in the range, so a resulting computation for a range from 5 to 10 would look like (((((0 + 5) + 6) + 7) + 8) + 9) + 10.

What if we now decided to modify this function to be more general and allow us to perform computations using different operations, for example multiply all numbers in the range and generate the following computation: (((((1 * 5) * 6) * 7) * 8) * 9) * 10? If you think about the differences between these two computations, you'll see that there are only two changes. First, we changed the initial value from 0 to 1 (because we don't want to multiply any result of a call by zero!) and we changed the operator used during the computation. Let's see how we could write a function like this in C#:

int SumNumbers(Func<int, int, int> op, int init, int from, int to) {
   if (from > to) return init;
   int sumRest = SumNumbers(op, initial, from + 1, to);
   return op(from, sumRest);

We added two arguments to the function - the initial value (init) and an operation that specifies what to perform with the intermediate result and a number from the range (op). To specify the second argument, we're using a delegate Func<int, int, int>, which represents a function that takes two arguments of type int and returns an int. In functional languages, we don't have to use delegates, because they have much simpler concept - a function. In fact, the generic Func delegate in C# 3.0 represents exactly the same idea. This is exactly what the term "functions as values" refers to - the fact that we can use functions in a same way as with any other data type available in the language. We can write functions that take functions as an argument (as we did in this example), but also return function as a result or even create a list of functions and so on. Now, before discussing the benefits of this approach, let's look at second key aspect that enables the declarative style, which is the syntax of functional languages.

Succinct and extensible syntax

As we already said, a key aspect of functional language syntax is that there is usually a small number of language constructs that are defined explicitly by the language specification. This means that many typical constructs are implemented as ordinary declarations in the library distributed with the language. As a result, you can implement a set of primitives, use them to construct your programs and the programs will look very much as if the primitives were standard part of the language.

To explain what this means in a practice, let's look at the following example. This time, we'll use a real F# code, which we'll implement later in the book. Demonstrating this on C# (or even non-existing "functional C#") would be quite difficult, because this aspect is easier to show on a language that was primarily designed with functional programming in mind.

let solarSystem =
   -- (rotate 80.00f 4.1f mercury)
   -- (rotate 150.0f 1.6f venus)
   -- (rotate 215.0f 1.0f
        (earth -- (rotate 20.0f 12.0f moon)))

Even without knowing anything about F#, you can see what the program describes. It defines our solar system with the first three planets (Mercury, Venus and Earth with Moon) and defines how the cosmic objects rotate round each other. If you think about the code for a while, you can see that Moon rotates around the Earth and Earth, together with two other planets, rotates around the Sun. The rotation is specified using two numbers and if you could play with the program, you would quickly recognize that the first number specifies how far the planet is from the center of rotation and the second specifies how fast it should rotate. You can see a screenshot demonstrating the application below

The most surprising fact about this example is that if you didn't know that the code is written in F#, you could think that it is written in some kind of simulation language. You could think that rotate is a keyword of this language (that defines how things rotate around each other) and that -- is some connector used in simulations. In fact, rotate is an ordinary F# function and -- is a custom operator, which you can easily define in F# according to your needs. Let's now look what do the last two concepts mean for the real-world development.

Benefits of the declarative style

The approach to define some basic constructs (like rotate and -- in our example) is very popular and powerful. In context of F# it is often called language oriented programming. This term points out, that we first design a "language", using which we later model our problems. Aside from the definition of the language, we also need a way for "executing" the model. In our example with solar system, the execution of the model means to show a window with an animation of planets, but you could imagine other means of execution, for example calculating possible intersections between planets and asteroids and many other possibilities.

Language Oriented Programmings

These languages are sometimes called DSLs (domain specific languages), which indicates that the language is designed to describe problems from some specific domain. In some sense, it gives us a vocabulary and grammatical rules for constructing the models. The key benefit of language oriented programming is that these languages can be quite simple and easy to explain (our animation DSL is a good example), so they can be in principle used even by those who understand the problem domain, but are not experts in functional programming. This means that those who use the DSL can be a distinct people from those who design it and develop it. Indeed, there are different requirements for people in both groups - the designers should be more advanced functional developers, whereas for the users, it is more important to well understand the problem domain. This is very important aspect for any pragmatic employer, who is considering using a functional programming language.

Of course, a language for writing animations is a very specific example. However, the same approach can be used for less specific problems, such as working with collections of data. In this example, we have a basic set of primitive constructs (operators) and we use them to specify what results we want to get. This highly relies on the possibility to use functions as values, because this allows us to specify part of the overall behavior of the operation as an argument. Another interesting aspect of the previous example is that it doesn't explicitly say how the animation should be executed and we'll talk about this in the next subsection.

Declarative problem solving

The approach in which we model what results do we want is called declarative, because we declare what properties should the result have instead of giving a step-by-step guide for the computer how to get the result. Even though this may sound a bit abstract, we can demonstrate it using a very well known example. Let's look at the following SQL query:

SELECT [ID], [Name]
   FROM [Customers]
   WHERE [City] = "London"

This is a typical example of declarative programming style. It uses standard operators (SELECT and WHERE) to describe what results we want to get. The operators are parameterized by giving them two "functions" as arguments, even though we don't write them as functions explicitly in SQL. The function is used as an argument to WHERE and it is a predicate specifying what customers should be selected. The function that parameterizes the SELECT operator specifies what properties of the customer should appear in the result.

The difference between SQL and declarative development in functional languages is that SQL works with a built-in set of primitive operators (such as SELECT and WHERE). On the other side, in functional languages, you can design and implement operators on your own for essentially any problem domain. Of course, this is exactly the idea of separating a language for solving problems from writing a code that solves a particular problem using that language, which we described above when talking about language oriented programming.

Let's now look at the benefits of the declarative style. First of all, using this style it is possible to represent the ideas in a much terser way. This of course leaves more work to the underlying library and the computer, which is however the preferred way in those days. Instead of specifying unnecessary details, we want to the development process more productive and foolproof and the benefits that can be gained by executing the program for example automatically in parallel fully compensate the overhead that we may get by leaving more work to the underlying library.

We've seen that using declarative style and language oriented programming, the program is composed from simple operations (that is our "vocabulary"). Some of the functional languages have rather sophisticated ways for specifying how the simple operations can be composed together and for ensuring that this is done correctly. This is done using types and we'll talk about it in the next section.


In this article I shortly discussed the idea behind declarative programming style. The key thing about declarative style is that the program expresses "what should be don" rather than "how exactly it should be achieved". This makes the code more readable and also easy to implement. Once you've seen the planet simulation I presented here, you don't have to be an F# expert to be able to add other planets.

I also explained how the declarative style relates to functional programming. The ability to use functions as values means that you can hide a lot of repetitive parts of code in a reusable unit. Many modern languages including C# now include some of functional features and you can find a lot of inspiration in languages like F# that makes you a better programmer even when working in C#.

About the Author

Tomas Petricek

Tomas Petricek discovered functional programming as a graduate student at Charles University in Prague. He has been a Microsoft C# MVP since 2004 and is one of the most active members in the F# community. In addition to his work with F#, he has been using C# 3.0 in a functional way since the early previews in 2005. He interned with the F# team at Microsoft Research, and he has developed a client/server web framework for F# called F# WebTools. His articles on functional programming in .NET and various other topics can be found at his web site http://tomasp.net.



  • You must have javascript enabled in order to post comments.

Leave a Comment

Top White Papers and Webcasts


Most Popular Programming Stories

More for Developers

RSS Feeds
