4.2. Contracts

A function contract is a comment that tells precisely what the function accomplishes and how its parameters affect what it accomplishes. It also tells requirements that it has on parameters.

A contract is suitable for exporting to library documentation, assuming that the function is intended to be part of a library. It tells someone how to use the function, not how the function works.

Someone who reads a contract is assumed to be able to see the function heading, but not the function body. Therefore:

A contract cannot refer to a function's body. It cannot refer to local variables in the body. It cannot refer to loops, if-statements, or anything else in the function's body.

Here are some do's and don'ts that should help you write clear, precise and concise contracts.

Do make sure to discuss the role of each parameter.

Don't just say that the parameter exists. Don't tell its type. That information is obvious from the heading.

Say how each parameter affects what the function accomplishes. If a parameter is an out-parameter, make that clear; say what is stored in it.


Do refer to parameters by their names.

Do not use pronouns or vague phrases such as "the number" or "the graph" to refer to parameters. Do not refer to a parameter by its type. Always refer to a parameter name by its name.

I recommend that you write parameter names in single-quote marks, such as 'size'. That makes it clear that you are referring to a parameter. (For single-letter parameters, you can do that, but it is not really needed.)


Do describe what the function returns.

It is not enough to say that a function returns "an integer". That is obvious from the heading. Say what the returned value means to the function's caller and how the parameters affect it. For functions with a void return type, nothing is returned, so skip this part.

Do discuss requirements.

If the function has requirements, be sure to mention them. For example, if parameter x must be a positive number, say that x > 0 is required. The caller needs to know that. If some data structure must have been set up in a particular way, say so. It can be useful to tell the reader that some other function must have been called before calling this one.

Do mention side-effects.

If the function has a visible effect (other than what it does with its own local variables) be sure to mention that effect. For example, if a function writes something to the standard output, then the caller needs to know that. It is not necessary to mention side-effects that are only done when tracing is turned on.

Do provide examples.

Showing some examples of what the function does can be very helpful to a reader. If a function reads a file in a particular format, show what a sample input file looks like. If it takes a number and yields a number, show some examples. If it takes a list and returns another list, show what it returns on sample lists.

Do write a contract before you write the function definition.

Before you try to solve a problem, find out what the problem is. A contract specifies just what problem the function solves. Get it down in writing before you write the function definition. If you do not understand what the problem is well enough to describe it, then you certainly do not understand the problem well enough to write a computer program to solve it.

Do modify the contract if you modify what the function does

A common mistake is to write a contract and then ignore it during the process of making changes to the program. Keep contracts up to date.

Do use correct spelling and grammar

A program with spelling or grammatical errors in contracts looks very unprofessional. Serious errors can make the contract unreadable. Be sure your contract does not look like someone just threw a bucket of words at the screen.

Don't say how the function works.

A contract is intended for someone who only wants to use the function. Do not burden that person with information about how it accomplishes what it does. If you only want to drive a car, you do not need to know how the engine works.

Don't say which other functions this one uses.

A contract does not tell how a function works. Saying that it uses f, g and h is irrelevant. That is also not an acceptable substitute for saying what it does.

Don't say which other functions use this function, or where the parameters come from.

It is tempting to try to describe just where a function fits into a program. For example, if you know that a particuar parameter will be a number that was read from the user, then it is tempting to say so in the contract. Don't.

Functions are tools that are part of a toolkit for a particular piece of software (or library). You might have a wrench in your toolbox that you acquired in order to turn a particular nut. But don't limit it to turning that nut. It will probably come in handy for some other nut later. Similarly, a function that you use in one place can often come in handy later at a different place. The function's contract should not limit the function's usefulness.

If a parameter is an array that was filled in by reading a file, do not call it a file. It is an array. Say what the function does with the array.


Don't discuss local variables of the body.

Assume the reader cannot see the body.

Don't describe the types of the parameters.

Type information is clear from the heading. Assume that the reader can see the heading. If there is any information that a person needs to know that is not present in the types, then describe that. It is a requirement.

Do not write a full function heading, with types, in the contract. That is not useful. But you often show the function with parameters, by name. For example, you might say that "factorial(n) returns n!". Notice that you don't say "factorial(int n) returns n!".

When you refer to a parameter, do not write its type. Use the parameter's name.



Exercises

  1. Is the following a sensible contract and heading for function distance?

      // distance(x,y,u,v) returns the distance between
      // points (x,y) and (u,v) in the plane.
    
      double distance(double x, double y, double u, double v)
    
    Answer

  2. Is the following a sensible contract and heading for function distance?

      // distance returns the distance between
      // points ('x','y') and ('u','v') in the plane.
    
      double distance(double x, double y, double u, double v)
    
    Answer

  3. Is the following a sensible contract and heading for function distance?

      // distance(x,y) returns the distance between
      // points (x,y) and (u,v) in the plane.
    
      double distance(double x, double y)
    
    Answer

  4. Is the following a sensible contract and heading for function distance?

      // distance(x,y,u,v) returns the distance between
      // the first two doubles and the second two doubles.
    
      double distance(double x, double y, double u, double v)
    
    Answer

  5. Is the following a sensible contract and heading for function distance?

      // distance(x,y,u,v) returns the distance between
      // two points in the plane.
    
      double distance(double x, double y, double u, double v)
    
    Answer

  6. Is the following a sensible contract and heading for function f?

      // f(x,y) takes two integers x and y and returns
      // an integer.
    
      int f(int x, int y)
    
    Answer

  7. Is the following a sensible contract and definition for function foo?

      // foo returns the sum of 'x' and 'y'.
    
      int foo(int x, int& y)
      {
        y = y + 1;
        return x + y;
      }
    
    Answer