PL/I Programming Style


  1. Comments and Whitespace.
  2. Indentation.
  3. Naming Conventions.
  4. Declarations.
  5. Program Structure.
  6. Preprocessor.
  7. Efficiency.
  8. Error handling.
  9. Maintenance.
  10. Example.
  11. Programming Style Literature.
  12. Don't like my style?

Copyright 2001 by Peter Flass. Permission is granted to distribute unmodified copies of this document. Please do not make local modifications. Suggest changes to the maintainer.

Comments and Whitespace

Comments are important to understanding; use them liberally.

The first line of each program or package should be a comment containing the name and a short description of the program. Many compilers will display the first line in a heading on each page.

Each external procedure or package should begin with a comment block noting the purpose of the procedure and adding additional descriptive information. This description should be defined by installation standards and/or the intended use of the procedure, but may contain some or all of the following information:

Use the preprocessor %PAGE statement to force long procedures, DO and SELECT groups, etc. to begin on a new page set off by comments briefly describing the section. For easy reading a logical function should be no longer than about two pages (120 lines of code).

It is not necessary to code a comment per line as in assembler. Logical functions within the code should be indicated by appropriate comments and separated by whitespace. Comments should say what  is being done, and not how  it is done.

Label long DO and SELECT statements and terminate them with the END <label> form of the END statement. Alternatively, use comments on END statements to indicate what  the END refers to. A loop headed

DO WHILE(a); could be ended 
END; /* DO WHILE    */ or 
END; /* DO WHILE(a) */

When coding comments on the same line as code, try to maintain consistent column positions for the /* and the */ from line to line.

Each comment line should be self-contained - contain both /* and */.

Comment null statements used as actions in THEN, WHEN, or OTHERWISE to indicate that this is intended and not an error. (See Example).

Whitespace is vital to readability. Too much as well as too little whitespace can make a program difficult to read.

Some programmers like to use blank lines to delimit related sections of code. A DO group of more than a line or two could have blank lines before the DO and after the END.

Too much whitespace eats up screen real estate when editing a program or spreads a listing over too many printed pages to be easily followed.

The attached example illustrates a suggested use of comments and whitespace, as well as other style suggestions.



For maximum compatibility, program statements should be contained between columns two and seventy-two of each line. Do not break tokens (words, strings, etc.) between lines. Split long strings into line-sized sections and use the concatenation (||) operator. Strings in INITIAL lists can be concatenated by enclosing the expression in another level of parentheses:
INITIAL( ('a' || 'b') )

Some compilers use the first column of each line as a forms-control character for the program listing. This should be considered non-portable. Use of preprocessor listing control statements (%PAGE, %SKIP) is preferable.

Indentation helps to distinguish related sections of a program. Code only one statement per line. Use consistent indentation (e.g. four spaces, or one tab-stop) to identify nested control structures. If indentation becomes excessive this is an indication that structures are nested too deeply and some code should be moved to a procedure and called.

There are two systems for relating ENDs to block headers. The first system places the END in the same position on a line as the corresponding block header, with the body of the block indented. The second indents all of the block including the END, and places the first statement following the block in the same column as the block header (see Example). Whichever system is used should be used consistently.

Align IF to the same column as the associated THEN and ELSE . The exception to this is a "laddered" if statement:

IF condition1 
THEN statement1; 
ELSE IF condition2 
     THEN statement2;
ELSE ...

which might be aligned with all the ELSEs in the same column as the IF and the initial THEN. Note that this statement is exactly the same as a SELECT:
    WHEN(condition1) statement1; 
    WHEN(condition2) statement2;
    END; /* select */

which is preferable.

Indent WHEN and OTHERWISE from the SELECT. Place a short one-statement action on the same line as the WHEN or OTHERWISE, otherwise place it on the next line indented from the WHEN.

Comments describing a section of code should be indented to the same column position as the code.


Naming Conventions

Since case is not significant in PL/I, upper/lower case can be used freely to improve readability, but use consistent orthography - do not use 'Test', 'test' and 'TEST' for the same variable. Use meaningful names, not necessarily long names. Avoid using PL/I keywords as variable names.



All data should be declared. Use a standard %INCLUDE file with declarations for all BUILTIN functions. Declarations should be coded at the beginning of a procedure, before the first executable statement.

Declare one structure element per line. Use indentation to show the structuring. Some programmers like to skip level numbers in declarations (1,5,10 instead of 1,2,3) to allow for future modifications.

Keep declarations simple; minimize factoring of attributes. Declare one variable or a set of variables with identical attributes per declaration statement.

Organize declarations. For example, code parameter declarations first, then file declarations followed by local DYNAMIC storage, with declarations of EXTERNAL items at the end. Using a consistent scheme makes it easy to locate information.

Use standard card columns for declarations. One possibility is to begin DCL in column 2, variable names in column 10, and attributes in column 30, aligned if more than one line is required for the attributes.


Program Structure

Structure is an important element of program style. There are two common systems of structuring a program. One places the first executable statement at the beginning of the program, immediately following the declarations, and follows the last executable statement, just prior to the final END, with definitions of all internal procedures. The second system follows the declarations with internal procedure definitions, and places the beginning of the mainline code following, similar to the structure of a Pascal program. Since PL/I is a two-pass compiler there is no technical preference.

Declarations can be placed at the start or the end of a procedure. Wherever they are placed they should be grouped together and not scattered throughout the procedure. One exception to this is that some programmers like to declare "their" data at the start of a procedure and %INCLUDEs for standard data at the end. This has the advantage that the standard definitions are easy to bypass when looking at a listing.

Place ON statements at the beginning of the program following the declarations and immediately prior to the first other executable statement. If proper operation of the program requires a different placement, comments should be used to indicate this.

Do not use multiple block termination. Each block or group should have its own END statement.

Always include an OTHERWISE clause in a SELECT-group. The default system action is to issue a not-particularly-helpful error message and terminate the program. If nothing else you might want to print the value of the expression causing the error, or specify a null statement as the action.



The PL/I preprocessor is a very powerful feature. Use the preprocessor to simplify parameterization of the program. Using:

     %DECLARE array_size CHARACTER;
     DECLARE an_array (array_size) FIXED;
will allow the compiler to generate the most efficient code for references to an_array, as opposed to declaring it CONTROLLED with dimension(*) and allocating it or passing array_size as a parameter.

It is obviously more efficient to select code to be generated at compile time rather than test at run-time:

     %IF debug='YES' %THEN %DO;
        Generate debug code here ...

Declare and initialize all preprocessor variables before the start of the program if possible. Document the use of all preprocessor variables as program parameters.



Efficiency is often machine- and compiler-dependent. There is no substitute for understanding your system.


Error Handling

Let PL/I error handling facilities handle those errors that are not expected; handle expected errors in-line. For example, if it is expected that "denom" could be zero, rather than relying on ZERODIVIDE, use:

     IF denom=0 THEN DO;
       /* whatever is required */
     ELSE result = num / denom;

Do the minimum possible processing in an ON-unit. Try to keep ON-units to a single statement:

Be sure to disable the condition being handled in an ON-unit to prevent recursive errors.

       /* blah, blah, blah */
       END; /* ON ERROR */



Nearly every program will need maintenance and modification. The following procedures may be used to track modifications:



Col: .........1.........2.........3.........4.........5.........6.........7.........8

      /* EXAMP: This program illustrates style suggestions                */
      /*                                                                  */
      /* Module:        EXAMP                                             */
      /* Author:        Peter Flass,  6 Jun 2001                          */
      /* Purpose:       This is a do-nothing program that illustrates some*/
      /*                of the the style suggestions in this document.    */
      /* Modifications:                                                   */
      /*                V1.0 - 7 Jun 2001 - change for blah, blah, blah   */
      /*                                                                  */

      EXAMP: procedure(parm) options(main);

      /*      Declare a major data structure like this                    */
      dcl   1 a_major_data_structure,
              2 a_minor_structure,                                  /*V1.0*/

      dcl    (name1,name2)        fixed bin(15,0) 
      dcl     name3               fixed bin(15,0);

      /*      Indicate a major program block like this                    */
      call initialize;

      if a=1            /* Comment null statements used as actions        */
      then /* do nothing */ ;    
      else blah, blah, blah ...

      call process_the_data;
      call finish_up;

      /*      Initialize: Describe the function of the 'initialize'       */
      /*                  procedure in this comment ...                   */
      initialize: procedure;

        /* Use distinctive comments to set */
        /* off smaller program sections.   */
        do i=1 to 5;    /* This illustrates one indentation scheme        */
            some stuff  /* Use one or the other, not both                 */
            some more stuff
        end; /* do i */
        next statement...

        do j=1 to 5;    /* This is the second indentation scheme          */
            some stuff
            some more stuff
            end; /* do j */
        next statement...

        call scan_parms;
        Do some stuff here ...

        /* Etc., etc.                      */
        Maybe do some more stuff ...
        call open_files;

        end initialize;

      end EXAMP;


Programming Style Literature

The following article contains an extensive bibliography of literature on programming style:
  Thomas, Edward J. and Oman, Paul W.
 "A Bibliography of Programming Style"
 ACM SIGPLAN Notices, Feb 1990; 7-16.

back Version 1.0b
Sep 2009 (editorial changes)