3.11 Style and Prettyprinting

All the program examples thus far have followed a certain written style. The beginner should study carefully the textual layout (also known as prettyprinting conventions) employed for these examples. Observe particularly the use of indentation and blank lines. These conventions are not necessary to produce working programs (the compiler couldn't care less about prettyprinting). Rather, they are required to produce readable programs.

Style is a matter of good taste, to be sure, but it is more than that: it is a matter of professional ethics as well. Programs written for a client must be readable and modifiable. That is why it is important to incorporate informative and effective comments in the body of programs. Non-trivial commenting for understandability and prettyprinting for readability go hand-in-hand with good overall design. Without these, one cannot produce programs that can be maintained, and cannot therefore produce good programs. Comments included in a program to explain its steps are part of its internal documentation. Because of the difference it makes in readability, prettyprinting is also part of internal documentation.

Consider an extreme example of unreadability. Since a Modula-2 compiler ignores carriage returns, a program could be written as:

MODULE Test; BEGIN Statement Sequence; END Test.

that is, all on one line. In practice, this would make things very nearly impossible to read, and even though the compiler would accept it, no computing science teacher or programming supervisor ever would.

Here are some general rules for laying out programs in a clear and readable fashion:

A. Prettyprinting Rules

1. Blank lines should be placed before such headings as CONST, VAR, TYPE, and PROCEDURE. (The latter two will be taken up later.) They should also be placed before and after any comment extending over more than one line. They may also be placed after each END but the last if desired. Creative and liberal use of blank lines to set off sections of code is encouraged.

2. Spaces should be placed before and after all arithmetic operations and the symbols ":=", "=", and ":", before the opening parenthesis which follows the name of a procedure (such as WriteString), and after any commas (say, in import lists.)

3. All statements and declarations begin on a new line.

4. The body of every CONST, VAR, and TYPE declaration as well as of every BEGIN .. END, IF .. THEN .. END or similar construction, is indented one tab or two spaces from the heading. The END of such a structure always appears directly below the keyword that starts it (such as THEN, or DO).

5. Specifically, the correct indentation for an IF .. THEN .. ELSIF .. ELSE is as follows:

 IF Boolean Expression 1
   THEN
     Statement Sequence 1;
   ELSIF Boolean Expression 2 THEN
     Statement Sequence 2;
     ...

   ELSE
     Statement Sequence n + 1;
   END;

In this construction, only the first THEN appears on a line by itself. Strictly speaking, it is an integral part of the first IF, and that is why there must be no semicolon before it, but lining THEN up with the ELSIF and ELSE keywords makes the whole block easier to read. The idea is to improve the layout, not to emphasize the THEN as if it were a more important part of the statement.

6. If a FROM is used, the list of imports should start on the line following the library module name.

7. When DO is used, it is indented on a line by itself. This applies to WHILE loops, and to any similar construction.

8. No source line should contain more than about 72 characters.

Besides these layout rules, there are a number of other style rules that should be followed in the choice of identifiers:

B. Identifier Style Rules

1. Use an identifier name which is self-documenting, such as Principal, rather than a cryptic character such as P.

2. The identifiers of constants and variables start with a lower case letter and are nouns; those of Procedures start with upper case letters. If they perform an action (eg WriteString) they are verbs and if they return a value (eg ReadResult) they are nouns. BOOLEAN identifiers should normally be adverbs.

3. Multi-word identifiers have each new word after the first starting with a capital (eg WriteString). The low-line often used in Pascal is allowed in ISO standard Modula-2, but its use ought to be discouraged by anyone interested in readable programs. Write myIdentifier, rather than my_identifier.

4. Only reserved words and standard identifiers are entirely capitalized, except in the case of certain imports, where the programmer must simply match the library style. (Authors of library modules should themselves follow this rule.)

5. Module names may have any case, as there may be constraints imposed by the underlying operating system.

Here is a bad example:

MODULE BadGradePoint;
FROM STextIO IMPORT WriteString,WriteLn,ReadChar;
FROM SWholeIO IMPORT WriteCard;
VAR x:CHAR;y:CARDINAL;
BEGIN WriteString("What is your letter grade?  A .. F ==>");
ReadChar(x);x:=CAP(x);IF x="A"THEN y:=4 ELSIF x="B"THEN y:=3
ELSIF x="C"THEN y:=2 ELSIF x="D"THEN y:=1 ELSE y:=0 END;
WriteString("The grade point for that letter grade is ");
WriteCard (y,2); WriteLn;IF y>2 THEN WriteString ("You passed.");
END;END BadGradePoint.

Could you tell what that program was for? (It did compile!) Here it is again in a better form:

MODULE BetterGradePoint;

(* This program takes a letter grade input from the keyboard and converts it into a grade point numeral. *)

FROM STextIO IMPORT
  WriteString, WriteLn, ReadChar;
FROM SWholeIO IMPORT
  WriteCard;

VAR
  userInput : CHAR;
  gradePoint : CARDINAL;

BEGIN
  (* First obtain the letter grade *)
  WriteString ("What is your letter grade?  A .. F ==>");
  ReadChar (userInput);
  userInput := CAP (userInput);

  (* and now, convert it to the correct number of points *)
  IF userInput = "A"
    THEN
      gradePoint := 4
    ELSIF userInput = "B" THEN
      gradePoint := 3
    ELSIF userInput = "C" THEN
      gradePoint := 2
    ELSIF userInput = "D" THEN
      gradePoint := 1
    ELSE
      gradePoint := 0
    END;

  (* output results *)
  WriteString ("The grade point for that letter grade is ");
  WriteCard (gradePoint, 2);
  WriteLn;

  IF gradePoint > 2  (* do this if grade point is high enough *)
    THEN
      WriteString ("You passed.");
    END;

END BetterGradePoint.

NOTE: The degree to which prettyprinting and style rules are enforced (as well as the details of these rules) depends not on the implementation of the language, but on the installation (site) at which it is used. Teachers or supervisors will surely vary these rules, but students and other low apprentices do so at their own peril.


Contents