Software Contracts, Part 6: Annotations

In short, an "annotation" is an addition to the source code for a program that allows an external translator to enforce the program's contract.

Way back in the beginning, the ONLY way to discover a function's contract was by it's documentation.  A function's contract looked like (from the MS-DOS 2.0 reference manual):

Get Time (Function 2CH)
    Call:
    AH = 2CH

    Return:
    CH
        Hour (0-23)
    CL
        Minutes (0-59)
    DH
        Seconds (0-59)
    DL
        Hundredths (0-99)

Function 2CH returns the current time set in the operating system as binary numbers in CX and DX [...] Depending on how your hardware keeps time, some of these fields may be irrelevant.  As an example, many CMOS clock chips do not resolve more than seconds.  In such a case the volume in DL will probably always be 0.

There was no other mechanism to let you know what a function expect (ok, your program would crash if you passed in invalid input, but that isn't particularly useful).

That's the problem with documentation-only contracts - they're extraordinarily fragile.  As a result, starting way back then (in the 1950s and 1960s) language designers started adding annotations to computer languages to help ensure that the contract for functions was met.

You've all seen these annotations - for instance, the first non trivial program in my copy of "A Practical Introduction to Pascal" published in 1978 has:

PROCEDURE DRAWALINE(LENGTH : INTEGER)
   VAR I : INTEGER;
   BEGIN
   FOR I := 1 TO LENGTH DO
      WRITE('-');
   WRITELN
   END;

The annotation is, of course the declaration of "length" as an integer.  When languages added type information to function prototypes, the type information functioned as annotations which allowed a software translator (the compiler) to enforce the contract.  It turns out that the compiler was in an ideal position to enforce the contract - by simply refusing to convert your high level program to machine code, it was quite simple for the compiler to ensure that you didn't violate the functions contract (too much).

Now the enforcement of the "type" contract varied from language to language.  For example the Pascal compiler was quite strict about its enforcement of annotations - if the parameters provided to a function didn't strictly match those of the function it refused to compile the code.  The "C" language compiler, on the other hand, was rather lax about enforcing parameters - for instance, the caller of a function didn't actually HAVE to match the number of parameters in the declaration of the function.  Unfortunately this rather lackadaisical attitude meant that it was easy to write some rather awkward code.  In addition, it turns out that it was somewhat difficult to translate C's semantics to some computer architectures (RISC machines, in particular).  As a result, in the 1990's as a part of the standardization of the C language, it also was changed to be strongly typed.

Type safety isn't a requirement - there are many languages that are essentially type-neutral (most scripting languages appear to be typeless (or have relatively weak type systems), for example), but that just means that it is harder to enforce strong contracts.

Next: Language Annotations: Beyond Simple Types