Programming in D – Tutorial and Reference
Ali Çehreli

Other D Resources

Contract Programming

Contract programming is a software design approach that treats parts of software as individual entities that provide services to each other. This approach realizes that software can work according to its specification as long as the provider and the consumer of the service both obey a contract.

D's contract programming features involve functions as the units of software services. Like in unit testing, contract programming is also based on assert checks.

Contract programming in D is implemented by three types of code blocks:

We will see invariant blocks and contract inheritance in a later chapter after covering structs and classes.

in blocks for preconditions

Correct execution of functions usually depend on whether the values of their parameters are valid. For example, a square root function may require that its parameter cannot be negative. A function that deals with dates may require that the number of the month must be between 1 and 12. Such requirements of a function are called its preconditions.

We have already seen such condition checks in the assert and enforce chapter. Conditions on parameter values can be enforced by assert checks within function definitions:

string timeToString(in int hour, in int minute) {
    assert((hour >= 0) && (hour <= 23));
    assert((minute >= 0) && (minute <= 59));

    return format("%02s:%02s", hour, minute);
}

In contract programming, the same checks are written inside the in blocks of functions. When an in or out block is used, the actual body of the function must be specified as a body block:

import std.stdio;
import std.string;

string timeToString(in int hour, in int minute)
in {
    assert((hour >= 0) && (hour <= 23));
    assert((minute >= 0) && (minute <= 59));

} body {
    return format("%02s:%02s", hour, minute);
}

void main() {
    writeln(timeToString(12, 34));
}

A benefit of an in block is that all of the preconditions can be kept together and separate from the actual body of the function. This way, the function body would be free of assert checks about the preconditions. As needed, it is still possible and advisable to have other assert checks inside the function body as unrelated checks that guard against potential programming errors in the function body.

The code that is inside the in block is executed automatically every time the function is called. The actual execution of the function starts only if all of the assert checks inside the in block pass. This prevents executing the function with invalid preconditions and as a consequence, avoids producing incorrect results.

An assert check that fails inside the in block indicates that the contract has been violated by the caller.

out blocks for postconditions

The other side of the contract involves guarantees that the function provides. Such guarantees are called the function's postconditions. An example of a function with a postcondition would be a function that returns the number of days in February: The function can guarantee that the returned value would always be either 28 or 29.

The postconditions are checked inside the out blocks of functions.

Because the value that a function returns by the return statement need not be defined as a variable inside the function, there is usually no name to refer to the return value. This can be seen as a problem because the assert checks inside the out block cannot refer to the returned variable by name.

D solves this problem by providing a way of naming the return value right after the out keyword. That name represents the very value that the function is in the process of returning:

int daysInFebruary(in int year)
out (result) {
    assert((result == 28) || (result == 29));

} body {
    return isLeapYear(year) ? 29 : 28;
}

Although result is a reasonable name for the returned value, other valid names may also be used.

Some functions do not have return values or the return value need not be checked. In that case the out block does not specify a name:

out {
    // ...
}

Similar to in blocks, the out blocks are executed automatically after the body of the function is executed.

An assert check that fails inside the out block indicates that the contract has been violated by the function.

As it has been obvious, in and out blocks are optional. Considering the unittest blocks as well, which are also optional, D functions may consist of up to four blocks of code:

Here is an example that uses all of these blocks:

import std.stdio;

/* Distributes the sum between two variables.
 *
 * Distributes to the first variable first, but never gives
 * more than 7 to it. The rest of the sum is distributed to
 * the second variable. */
void distribute(in int sum, out int first, out int second)
in {
    assert(sum >= 0);

} out {
    assert(sum == (first + second));

} body {
    first = (sum >= 7) ? 7 : sum;
    second = sum - first;
}

unittest {
    int first;
    int second;

    // Both must be 0 if the sum is 0
    distribute(0, first, second);
    assert(first == 0);
    assert(second == 0);

    // If the sum is less than 7, then all of it must be given
    // to first
    distribute(3, first, second);
    assert(first == 3);
    assert(second == 0);

    // Testing a boundary condition
    distribute(7, first, second);
    assert(first == 7);
    assert(second == 0);

    // If the sum is more than 7, then the first must get 7
    // and the rest must be given to second
    distribute(8, first, second);
    assert(first == 7);
    assert(second == 1);

    // A random large value
    distribute(1_000_007, first, second);
    assert(first == 7);
    assert(second == 1_000_000);
}

void main() {
    int first;
    int second;

    distribute(123, first, second);
    writeln("first: ", first, " second: ", second);
}

The program can be compiled and run on the terminal by the following commands:

$ dmd deneme.d -w -unittest
$ ./deneme
first: 7 second: 116

Although the actual work of the function consists of only two lines, there are a total of 19 nontrivial lines that support its functionality. It may be argued that so much extra code is too much for such a short function. However, bugs are never intentional. The programmer always writes code that is expected to work correctly, which commonly ends up containing various types of bugs.

When expectations are laid out explicitly by unit tests and contracts, functions that are initially correct have a greater chance of staying correct. I recommend that you take full advantage of any feature that improves program correctness. Both unit tests and contracts are effective tools toward that goal. They help reduce time spent for debugging, effectively increasing time spent for actually writing code.

Disabling contract programming

Contrary to unit testing, contract programming features are enabled by default. The ‑release compiler switch disables contract programming:

dmd deneme.d -w -release

When the program is compiled with the ‑release switch, the contents of in, out, and invariant blocks are ignored.

in blocks versus enforce checks

We have seen in the assert and enforce chapter that sometimes it is difficult to decide whether to use assert or enforce checks. Similarly, sometimes it is difficult to decide whether to use assert checks within in blocks versus enforce checks within function bodies.

The fact that it is possible to disable contract programming is an indication that contract programming is for protecting against programmer errors. For that reason, the decision here should be based on the same guidelines that we have seen in the assert and enforce chapter:

Exercise

Write a program that increases the total points of two football (soccer) teams according to the result of a game.

The first two parameters of this function are the goals that each team has scored. The other two parameters are the points of each team before the game. This function should adjust the points of the teams according to the goals that they have scored. As a reminder, the winner takes 3 points and the loser takes no point. In the event of a draw, both teams get 1 point each.

Additionally, the function should indicate which team has been the winner: 1 if the first team has won, 2 if the second team has won, and 0 if the game has ended in a draw.

Start with the following program and fill in the four blocks of the function appropriately. Do not remove the assert checks in main(); they demonstrate how this function is expected to work.

int addPoints(in int goals1,
              in int goals2,
              ref int points1,
              ref int points2)
in {
    // ...

} out (result) {
    // ...

} body {
    int winner;

    // ...

    return winner;
}

unittest {
    // ...
}

void main() {
    int points1 = 10;
    int points2 = 7;
    int winner;

    winner = addPoints(3, 1, points1, points2);
    assert(points1 == 13);
    assert(points2 == 7);
    assert(winner == 1);

    winner = addPoints(2, 2, points1, points2);
    assert(points1 == 14);
    assert(points2 == 8);
    assert(winner == 0);
}

Note: It may be better to return an enum value from this function:

enum GameResult {
    draw, firstWon, secondWon
}

GameResult addPoints(in int goals1,
                     in int goals2,
                     ref int points1,
                     ref int points2)
// ...

I chose to return an int for this exercise, so that the return value can be checked against the valid values of 0, 1, and 2.