Export (0) Print
Expand All

Overview: Benefiting from Declarative Style

Visual Studio 2010

Applies to: Functional Programming

Published: January 2010

Authors: Tomas Petricek and Jon Skeet

Referenced Image

Get this book in Print, PDF, ePub and Kindle at manning.com. Use code “MSDN37b” to save 37%.

Summary: This overview introduces declarative programming style and explains how it makes programming easier. It features examples from existing .NET libraries such as LINQ or WPF and shows how to use the same principles when modeling financial contracts in F#. (8 pages)

This topic contains the following sections.

This article is an excerpt from Real World Functional Programming: With Examples in F# and C# by Tomas Petricek with Jon Skeet from Manning Publications (ISBN 9781933988924, copyright Manning Publications 2009, all rights reserved). No part of these chapters may be reproduced, stored in a retrieval system, or transmitted in any form or by any means—electronic, electrostatic, mechanical, photocopying, recording, or otherwise—without the prior written permission of the publisher, except in the case of brief quotations embodied in critical articles or reviews.

If you write programs or components using the functional programming style, they will become more declarative. When writing code in this way, you’ll express the logic of programs without specifying the execution details. But declarative programming is a more general idea that can be realized using different technologies. Functional programming is just one way to achieve that.

This overview starts by explaining what declarative programming is in abstract terms. Then, it explores what the approach means in practice by looking at two examples from the .NET world that many readers are already familiar with—LINQ and XAML. Finally, it briefly looks at an example that shows what declarative programming looks like in the F# language.

A program has to explain the goals of the programmer to the computer using the vocabulary that the computer understands. In imperative languages, this consists of commands. Programmers can add new commands, such as “show customer details,” but the whole program is a step-by-step description saying how the computer should accomplish the overall task. An example of a program is “Take the next customer from a list. If the customer lives in the UK, show their details. If there are more customers in the list, go to the beginning.”

NoteNote

Once the program grows, the number of commands in our vocabulary becomes too high, making the vocabulary difficult to use. This is where object-oriented programming helps because it allows organizing commands in a better way. It is possible to associate all commands that involve a customer with some customer entity (a class), which clarifies the description. The program is still a sequence of commands specifying how it should proceed.

Functional programming provides a completely different way of extending the vocabulary. Instead of just adding new primitive commands, it allows adding new control structures—primitives that specify how to put commands together to create a program. In imperative languages, commands are composed in a sequence or by using a limited number of built-in constructs such as loops. A typical program uses a number of other recurring structures—common ways of combining commands. In fact, some of these recurring structures are very well known and are described by design patterns. In imperative languages, developers keep typing the same structure of code over and over again.

In the previous example, the pattern could be expressed as “Run the first command for every customer for which the second command returns true.” Using this primitive, the program can be expressed simply by saying “Show customer details of every customer living in the UK.” In this sentence, “living in the UK” specifies the second argument, and “show customer details” represents the first.

Here are the two sentences that we’ve used to describe the same problem:

  • Take the next customer from a list. If the customer lives in the UK, show their details. If there are more customers in the list, go to the beginning.

  • Show customer details of every customer living in the UK.

This is the essential difference between imperative and declarative styles of programming. The second sentence has a far better readability and reflects more closely the aim of our “program.” The first sentence describes exactly how to achieve the goal, whereas the second describes what the desired result is.

The discussion about declarative style was quite general because the idea is universal and not tied to any specific technology. The next section turns from using an analogy to actual source code. It starts by examining how the declarative style is reflected in LINQ. Readers unfamiliar with LINQ don’t need to worry—the examples are simple enough to grasp without background knowledge. In fact, the ease of understanding code—even in an unfamiliar context—is one of the principal benefits of a declarative style.

For those already using LINQ, this example will be just a reminder. But, it demonstrates something more important. Here’s an example of code that works with data using the standard imperative programming style:

IEnumerable<string> GetExpensiveProducts() {
   // Create new list for storing the results
   List<string> filteredInfo = new List<string>();

   // Iterate over all products from the data source
   foreach(Product product in Products) {
      if (product.UnitPrice > 75.0M) {
         // Add expensive product to a list of results
         filteredInfo.Add(String.Format("{0} - ${1}",
            product.ProductName, product.UnitPrice));
      }
   }
   return filteredInfo; 
}

The code is written as a sequence of basic imperative commands. The first statement creates a new list, the second iterates over all products, and the last one adds the element to the list. We’d like to be able to describe the problem at a higher level. In more abstract terms, the code filters a collection and returns information about every returned product.

In C# 3.0, the same code can be written using query expression syntax. This version is closer to the real goal—it uses the same idea of filtering and transforming the data.

IEnumerable<string> GetExpenisveProducts() {
   return Products
            .Where(product => product.UnitPrice > 75.0M)
            .Select(product => String.Format("{0} - ${1}", 
                      product.ProductName, product.UnitPrice))
            .ToList(); 
}

The expression that calculates the result uses several basic methods such as Where or Select. These methods take other expressions as an argument because they need to know what we want to filter or select as a result. The expressions are passed as delegates and are written using the lambda function syntax arg => body. This means that the Where (or Select) method can call the function with different products as arguments to check whether certain product should be filtered.

Using the previous analogy, these methods offer a new way of combining pieces of code to express our intention with less writing. Note that the whole calculation is written as a single expression that describes the result rather than a sequence of statements that constructs it. This is a repeated trend in declarative libraries and languages. In more declarative languages such as F#, everything you write is an expression.

Another interesting aspect in the example is that many technical details of the solution are now moved to the implementation of the basic methods. This makes the code simpler but also more flexible because we can change the implementation of these methods without making larger changes to the code that uses them. As you’ll see further, this makes parallelizing the code that works with data much easier.

LINQ isn’t the only mainstream .NET technology that relies on declarative programming. The following section finds similar concepts in Windows Presentation Foundation and the XAML language.

Windows Presentation Foundation (WPF) is a .NET library for creating user interfaces. This library supports the declarative programming style. It separates the part of the application that describes the UI from the part that implements the imperative program logic. The best practice in WPF is to minimize program logic and create as much as possible in a declarative way.

The declarative description is represented as a treelike structure created from objects that represent individual GUI elements. It can be created in C#, but WPF also provides a more comfortable way that uses an XML-based language called XAML. Nevertheless, many similarities exist between XAML and LINQ. The next listing shows how the code in XAML compares with the code that implements similar functionality using the imperative Windows Forms library.

<!-- Declarative user interface in WPF and XAML -->
<Canvas Background="Black">
   <Ellipse x:Name="greenEllipse" Width="75" Height="75" 
      Canvas.Left="0" Canvas.Top="0" Fill="LightGreen" />
</Canvas>

// Imperative user interface using Windows Forms
protected override void OnPaint(PaintEventArgs e) {
   e.Graphics.FillRectangle(Brushes.Black, ClientRectangle);
   e.Graphics.FillEllipse(Brushes.LightGreen, 0, 0, 75, 75);
}

It isn’t difficult to identify what makes the first code snippet more declarative. The XAML code describes the UI by composing primitives and specifying their properties. The whole code is a single expression that creates a black canvas containing a green ellipse. The imperative version specifies how to draw the UI. It’s a sequence of statements that specify what operations should be executed to get the required GUI. This example demonstrates the difference between saying "what" using the declarative style, and saying "how" in the imperative style.

The declarative version hides many of the underlying technical details. To write the code, developers don't need to know how WPF represents and draws the GUI. In the Windows Forms example, all the technical details (such as the representation of brushes and the order of drawing) are visible in the code. In the snippet above, the correspondence between XAML and the drawing code is clear, but XAML with WPF can describe more complicated run-time aspects of the program:

<DoubleAnimation
   Storyboard.TargetName="greenEllipse" 
   Storyboard.TargetProperty="(Canvas.Left)"
   From="0.0" To="100.0" Duration="0:0:5" />

This single expression creates an animation that changes the Left property of the ellipse (specified by the name greenEllipse) from the value 0 to the value 100 in 5 seconds. The code is written in XAML, but we could have written it in C# by constructing the object tree explicitly. DoubleAnimation is just a class, so we would specify its properties. The XAML language adds a more declarative syntax for writing the specification. In either case, the code would be declarative thanks to the nature of WPF. The traditional imperative version of code that implements an animation would be rather complex. It would have to create a timer, register an event handler that would be called every couple of milliseconds, and calculate a new location for the ellipse.

The last two sections demonstrated two technologies from .NET that use the declarative style to simplify solving certain kinds of problems (data querying and creating of user interfaces, respectively). Now, you may be asking yourself how you use it to solve your own kinds of problems. The next section takes a brief look at an F# library that demonstrates this.

Functional programming can be used to create libraries for solving problems in the declarative style. The last two examples demonstrated how LINQ does that for data manipulation and how WPF does that for UIs, but, in functional programming, people often create libraries for their own problem domains.

It was mentioned earlier that declarative style makes it possible to ignore implementation details. That's not completely true. Functional programming doesn’t have any mystical powers that would implement the difficult part. The author of a library still needs to implement all the technical details when designing it. But, the implementation details can be hidden in the library (just like LINQ hides all the complexity) so the general problem can be solved once and for all. This section demonstrates a library for modeling financial contracts, described in detail in Real-World Functional Programming in Chapter 15.

Introducing Financial Contracts

The financial industry uses a large number of types of contracts including swaps and futures. A contract generally specifies what commodity is bought or sold, when, and at what price. An application for working with contracts could have hard-coded support for each of the known types of the contract directly. Unfortunately, there is a huge number of types, so this solution wouldn’t be flexible.

A better approach is to create a library that allows describing contracts in general. The most elementary component of a contract is a trade, so the library provides primitives for saying that a party is willing to make some trade—either purchasing or selling some amount of some commodity. For example, a trader is willing to buy 1,000 shares of Contoso, Ltd. stock. Contracts typically also specify when or under what conditions the trade can take place. Finally, it is also possible to compose contracts and say, for example, that a trader wants to buy some amount of Contoso, Ltd. stock but sell some Fabrikam, Inc. stock at the same time.

Modeling Financial Contracts in F#

LINQ uses a couple of primitives to define data transformations and WPF contains primitives to describe the user interface. Similarly, the F# library for modeling contracts contains several primitives that can be used to specify and compose trades as well as to add conditions.

The following example models a contract specifying that we want to sell 500 Fabrikam shares on April 15 and buy 1000 Contoso shares anytime between April 10 and April 19:

let april day = DateTime(2011, 4, day)
let itTrades =   
  combine (sell (tradeAt (april 15) "FABR" 500))
          (between (april 10) (april 19) (trade "COSO" 1000))

The snippet constructs a value named itTrades that describes a financial contract. The tradeAt primitive creates a contract that can occur only at the specified date. The sell operation turns a default buy contract into a contract that specifies selling. The other leg (a financial term used for parts of a composed contract) describes a trade and then adds a condition saying that it can occur only between the given dates. Finally, the combine primitive takes two contracts and merges them into a single one.

Even if you're not an F# expert, you can probably read the code and understand what it is specifying. This is one of the most notable benefits of the declarative style. Instead of understanding every detail of the programming language, it is much more important to understand the domain that we’re modeling. In practice, the library will be written by experts in both F# programming and the financial domain (or by F# experts in collaboration with financial experts). However, the people who will use the library to model contracts need to know the financial domain but can be quite inexperienced F# programmers.

Another aspect of the declarative style that can be seen in the code is that the contract is, in principle, described using a single expression. The snippet declares a helper function april to be more readable, but the occurrences of the function could be replaced with DateTime values and the contract would remain the same.

The specification of a contract can be used for various purposes. For example, we could estimate the risk of the contract or calculate the total value of the contract. The simplest thing is to evaluate the contract to see what trades occur at a given date:

> eval itTrades (april 14);;
val it : (string * int) list = [("COSO", 1000)]

> eval itTrades (april 15);;
val it : (string * int) list = [("FABR", -500); ("COSO", 1000)]

This snippet shows commands together with the results printed by F# Interactive. We use the eval function that takes a trade specification and a date that should be checked. The result is a list containing the trades to be executed. In the first case, a trader buys 1000 FABR stocks and, in the second case, we sell 500 COSO stocks and buy 1000 FABR stocks.

This snippet can be written without knowing anything about the representation of trades. The representation is hidden in the library. The whole trade specification is constructed just by composing several primitives. However, the number of primitives that the library needs to provide is surprisingly small. If the basic set of primitives is well designed, it can be relatively small but powerful enough so that it can specify any desired contract. The users of the library can then create their own derived functions to specify common types of financial contracts—for example, swaps and futures. In fact, the tradeAt function in the previous example is defined using two other primitives:

let tradeAt date what amount = 
  between date date (trade what amount)

To create a trade that can happen at the specified date, it just creates an unconstrained trade using the trade function and then limits the validity using the between primitive. If the starting date is the same as the ending date, the result is a contract that can occur only at the specified single day.

Keeping the number of primitives as small as possible is one of the key advantages of declarative style because it makes working with contracts (or other domain objects) simpler. For example, when implementing the evaluation of contracts demonstrated earlier, the author of the library only needs to cover few basic cases (such as trade or combine). Not a single additional line of code is needed to make evaluation of derived contracts work.

This overview discussed declarative programming, which is a style that is used by most well-written functional libraries. Declarative programming is not limited only to functional languages. In fact, there are quite a few .NET technologies that already use it. This article looked at LINQ and WPF, but there are several others. For example, many .NET libraries use .NET attributes to declaratively specify how they should work with .NET types. The most notable benefit of declarative programming is that programs specify “what” we want to get as the result rather than “how”. As a result, it is more important to understand the domain that we’re working with than every detail of the language.

The support for declarative programming isn’t the only benefit of functional languages. Other benefits are discussed in the next two sections:

These benefits arise from the fact that functional programming is based on a different set of core principles. The focus on immutable data types and expression-based programming changes the way we think about programs. The following articles give an overview of the functional concepts:

To download the code snippets shown in this article, go to http://code.msdn.microsoft.com/Chapter-1-Introducing-F-c967460d

This article is based on Real World Functional Programming: With Examples in F# and C#. Book chapters related to the content of this article are:

  • Book Chapter 1: “Thinking differently” contains a detailed explanation of the difference between the imperative and the functional programming concepts and explains the benefits of the functional approach.

  • Book Chapter 2: “Core concepts in functional programming” explains concepts that follow from the difference between "the execution of statements" and "evaluation expressions".

The following MSDN documents are related to the topic of this article:

  • XAML Overview (WPF) explains the XAML language used in an example above and highlights several declarative aspects of the language.

  • LINQ to Objects explains the benefits of using declarative LINQ for working with in-memory data and gives several examples.

  • Declarative Security demonstrates another way of writing declarative programs in .NET. It uses C# attributes to specify security properties of code.

Show:
© 2014 Microsoft