System iNetwork (formerly iSeries Network)
Home Site Map Contact Us My Profile Log In Join Now!
Info Centers
 Forums

 Tech Center

 News & Analysis

 Solution Center

 UK Centre
Popular Spots
 25th Anniversary

 Article Archive

 ProVIP Center (Club Tech)

 Code

 System i DocFinder

 Essential Guides

 Blogs

 Wikis

 e-Learning

 Webcasts

 Podcasts

 System i Jobs

 Events
Products
 i5 Route Finders

 Learning Center (Store)

 Product Directory
Network Poll
Determining a programmer's desktop requirements is not a black-and-white proposition, but matching equipment to programmer type can help productivity. Which "programmer type" are you?
Vote Now!
Network Memberships
 See Membership Levels

 Free E-Mail Newsletters

 Free RSS Feeds

 Subscribe/Join

 Upgrade Now

 Renew Now
About Us
 About the Network

 Network Publications

 Tech Editor Profiles

 Editorial Calendar

 Contact Us

 Subscribe

 Media Kit (PDF)

 Write For Us


System iNetwork July Sponsor





        System iNetwork July Sponsor


Home » Tech Center » Cobol Style Guide
  iSeries News Cobol Style Guide


TABLE OF CONTENTS

INTRODUCTION
Contents
Purpose
Cobol/400 and ILE Cobol/400
GENERAL LAYOUT
Introduction
Consistency and flexibility
Blank lines, white space, and other separators
IDENTIFIERS AND RESERVED WORDS
Capitalization
Parts of identifiers and abbreviations
Mnemonics (named constants)
PROGRAM LAYOUT
Program header
ILE Cobol nested programs
Environment Division
Working-Storage Section
Linkage Section
Procedure Division
CODING PRACTICES
Minimize global data in ILE Cobol
Don't use Goto's
Don't use Next Sentence
"Guarding" code
Modifying parameters and passing arguments by content
REUSABLE CODE
ILE Replace directive
Copy members
ADDITIONAL NOTES
Recursion
Default linkage type in ILE Cobol

INTRODUCTION

Contents
This document provides coding style guidelines for Cobol/400 and ILE Cobol/400 source code published in NEWS/400 magazine and distributed via electronic media.


Purpose
These style guidelines help make published code easier to understand, and reflect good programming practice. There are many reasonable alternatives to the specific guidelines in this document; however, by following these guidelines, published code is more consistent. Consistent code is easier for readers to follow because they don't have to reorient themselves to a new style with each article.


Cobol/400 and ILE Cobol/400
In the rest of this document, when it's necessary to distinguish beteen the non-ILE and ILE versions of Cobol/400, the non-ILE version is referred to as Cobol/400, and the ILE version is referred to as ILE Cobol. When a topic applies to both, this document uses simply Cobol.


GENERAL LAYOUT

Introduction
One of Cobol's strengths is its "English-like" syntax, including a generally free-format layout and support for long names. You should take advantage of Cobol's flexible syntax, while at the same time incorporating a more "structured" style where a structured style aids quick and accurate comprehension.


Consistency and flexibility
Try to use a consistent approach to your style. The principle of consistency doesn't mean all style rules are rigid or simple-minded. For example, rather than suggesting mechanical rules of indentation to apply to all situations, this guide suggests you use "adaptive" indentation so your source shows subordinate code with recognizable - but not overly deep - indentation.

Flexibility, on the other hand is no excuse for sloppiness or laziness. Don’t use flexibility as an excuse for not taking the time to align code or to revise variable names so they use consistent abbreviations and structure. One of the secrets to highly readable code is re-reading and revising your code to improve the clarity and layout.

Flexibility also means you may decide to use a different standard than the ones suggested in this document when you believe there’s a clearer alternative for writing easily comprehended code. In many places in this guide, alternative styles are suggested. Whichever style you decide to follow, be sure you follow it consistently, especially within all source members for a single application.

Over time, you may find ways to improve your style. Don’t change your style with every program you write, and never mix inconsistent styles in the same source member. But it’s OK to change your cosing "standards" from time to time, as you improve your coding style.


Blank lines, whitespace, and other separators
Consistent use of blank lines, whitespace, and other separators makes it easier to comprehend the organization of a Cobol program.

Dashed line comments
Use consistent single- or double-dashed-line comments to separate major parts of a program. An effective approach for dashed lines is to use alternating dashe characters (-) for a single-dashed line, and alternating equal characters (=) for a double-dashed line, as shown below:

* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

* = = = = = = = = = = = = = = = = = = = = = = = = = = = =

Solid rows of * should be used sparingly, if at all, since they create a dense image on the screen or listing that makes adjacent code harder to read.

As an example of how to use dashed lines:

  • Use double-dashed lines to separate ILE Cobol nested programs
  • Use single-dashed lines between:
    • Parts of the program header comments (see below)
    • Program divisions and sections
    • Procedure Division paragraphs.
Blank lines
Use a blank line to separate different parts of a program, and to make it easier to tell which statements form a related group:

  • File Select clauses (in the File-Control paragraph)
  • Group items or a set of related elementary items in Working-Storage
  • A set of related statements in the Procedure Division

When you need to provide even more visual separation for "chunks" of code, use two blank lines — but don’t overdo it. Use the two-blank-line separator sparingly — such as when you need to indicate a larger grouping of code within which you’ve used single blank lines to separate sub-groups.

Spaces
Use spaces (or Tabs, if you have a PC-based editor) to provide indentation and alignment (see below).

Optionally, you may want to improve the readability of arithmetic and Boolean expressions by using a space after each ( and before each ), as in the following examples:

Compute AmtDue = Principal * ( 1 - Discount )

if ( AmtDue > CreditLimit ) And
( CreditSts Not = CreditStsExcellent )
...


IDENTIFIERS AND RESERVED WORDS

Capitalization
Cobol allows upper and lower case characters in identifiers and reserved words, as well as the dash character () as a separator. Use mixed case because it’s easier to read than all lower or upper case.

Reserved words
Capitalize the first letter of each Cobol reserved word, except the connectors listed below. If a reserved word has a dash, capitalize the first letter and each letter immediately following a dash.

Unstring
Goback
Working-Storage

Alternative style: If a reserved word has a dash, capitalize only the first letter.

Working-storage

Connectors
Use lowercase for the following connectors:

are at by for from is of than through to with

Alternative style: Capitalize the first letter of all connectors, too.

Identifiers
Capitalize the first letter of each part of an identifier. Do not use the dash separator. Use lowercase for the of in qualified names.

CustomerName of InpRcd

Alternative style: Capitalize only the first letter of the identifier. Use the dash separator between parts of the identifier.

Customer-name of Input-record

The basis for using dashes to separate parts of a multi-word identifier is this style’s similarity to the use of a space to separate English words. With this style, it’s better to use mostly full words rather than lots of abbreviations.

Consider the examples below as you decide which style to adopt.

  1. UsrSpcLstNbrOfEntries
  2. UsrSpcLst-NbrOfEntries
  3. Usr-spc-lst-nbr-of-entries
  4. User-space-list-nbr-of-entries
  5. User-space-list-number-of-entries [invalid—too long]
Examples 1 and 4 are the ones that best exemplify the two suggested styles.


Parts of identifiers and abbreviations
Identifiers should be meaningful, but digestible. This requires balancing the use of unabbreviated and abbreviated terms so an identifier’s meaning is clear, yet the identifier isn’t so long that it’s unwieldy.

Parts of an identifier
Use a consistent term, either unabbreviated or abbreviated, for each part of an identifier. For example, do not use Cst, Cust, and Customer for "Customer" in the same program.

Abbreviations
Careful use of abbreviations is very important to code consistency and clarity. Although there are some helpful guidelines, the key to effective naming is to review you code as you work on it to see that the names are easy to understand and consistent.

For most abbreviations, use at least 3 characters. Where there’s a standard CL abbreviation, such as Dsp for "display", use that abbreviation. If there’s a good 3-character abbreviation, use it rather than a longer abbreviation (e.g., use Idx instead of Indx).

For new, 3-character abbreviations, form most abbreviations with the first letter of the abbreviated word and the two consanants that are most important in pronouncing the name. For example, use Hdr for "header".

Don’t simply foreshorten words to create abbreviations. The following examples demonstrate this point:

Bad style Good Style
ColmnHdng ColHdr
BufferPostn BufPos
The rules for forming abbreviations aren’t absolute. In some cases there may be an obviously more familiar or clearer abbreviation; for example, Buf is a familiar abbreviation for "buffer", and is preferable to Bfr It’s also preferable to use Cust (for "customer") and Cost (unabbreviated) in the same application, rather than Cst and Cost because you reduce the chance for confusion.

Generally, don’t mix abbreviated and unabbreviated parts in the same name. If you do use both, generally you should use all abbreviations for the first part(s) of the identifier and use all unabbreviated terms for the last part(s) of the identifier. Here are some examples of acceptable identifiers:

ApiFldUsrSpc or ApiFldUserSpace (but not ApiFldUserSpc)
UsrSpcBuf
or UsrSpcBuffer (but not UsrSpaceBuf)
FldTxt
, FldText, or FieldText (but not FieldTxt)

Most importantly, review your identifers to make sure you haven’t used a variety of approaches to abbreviation — especially in the same source member.

Prefixes and Suffixes
When a set of identifiers need to be grouped, use either a group item declaration and qualified names (of), or use a common prefix or suffix as part of the name. With this style, choose either prefixes or suffixes to group related identifiers—don’t use prefixes for some groups and suffixes for other groups. Using prefixes is generally preferable to suffixes.

Do not use WS or similar prefix to identify variables declared in Working-Storage—this is just "noise".

Do not use numbers as prefixes to program, section, or paragraph names. Names such as 100-READ-CUST-FILE are an antiquated, less readable style. The historical purpose of the numeric prefix was to make it easier to find a routine in a listing; however, alphabetizing routines within groups (see below) achieves the same effect.


Mnemonics (named constants)
Don't use "magic" values.
The only literals that should appear in Procedure Division statements are 0 (when initializing a magnitude, such as a count or length) and 1 (when incrementing a count). For other numeric or character constants, declare a meaningfully-named variable to serve as a mnemonic. Even if you use a constant only once in a program, you should declare a mnemonic for it. Mnemonics make it easier to understand the meaning of a statement, make code easier to maintain, and reduce the chance of errors from "typos".

You can group mnemonics under one or more 01-level items.

01 Constants.
02 YesSpcVal Like SpcVal Value "*YES".
02 SysMsgIdLen Like Integer Value 7.

You may want to put frequently-used mnemonics in a Copy member that you include at the beginning of the Working-Storage Section. Note that for constant declarations, which will all have a Value phrase, but not many other phrases, you can usually put the Value phrase on the same line as the Pic or Like phrase. The Value phrases should be aligned.

Alternative style: Define frequently-used mnemonics in the standard Replace directive (see below).

Zero
Use the digit 0, not the reserved word Zero. You wouldn’t write:

Compute RcdCnt = RcdCnt + One

Similarly, there's no need to use a word for the number 0. Zero just makes code more cumbersome to read. Note that you should use a meaningful mnemonic for 1, when it’s isn’t just used as a "unit". For example, if the minimum purchase on an order is one dollar, code::

01 MinOrdTot Like Dollar 1.00.
...
If OrdTot < MinOrdTot ...


PROGRAM LAYOUT

Program header
At the beginning of each source member, include a standard header that describes the program purpose, interface, and maintenance history. Immediately follow the source member header with a Copy for the standard Replace directive. Here’s an example from the beginning of a source member:

* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = 
 Id Division. 
 Program-Id. CustMaint. 
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
* Purpose. Customer maintenance 
* 
* Allows browse and update of Customer master file. 
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
* Parameters. 
* Name     Type       Usage  Comment 
* -------- ---------- ------ ------------------------------ 
* CustId   CustIdType In     If valid, only this customer 
*                            is displayed.  Otherwise, a 
*                            Customer list is presented for 
*                            user selection 
* RtnSts   RtnStsType Out    Standard return status structure 
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
* History. 
* Version   Date    Person Purpose 
* -------  ---------  ---  -------------------------------- 
* 1.00.01  13-Jun-95  PTC  Original version 
* 1.00.02  25-Aug-95  PTC  Added discount calc 
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
•	Copy StdReplace. 

For ILE nested programs, you can use an abbreviated version of the header.

Note that the Id Division only contains the Program-Id paragraph. The other paragraphs are optional and are generally better omitted.


ILE Cobol nested programs

In ILE Cobol, a program can contain "nested" programs. Nested programs provide a better alternative than paragraphs for organizing programs because they allow parameters and local variables. You should arrange nested programs so you get the intended scope of variables and nested program names.

Local variables (no Global attribute) can be referenced only within the same program. Global variables can be referenced anywhere within the same program or any program it contains.

Nested program names without the Common attribute can be Call’d only in the containing program.

Nested program names with the Common attribute can be Call’d in the containing program and any programs contained by the containing program.

In general, don’t use global variables. Use parameters to pass data between routines and use local variables for each routine’s private work variables. The major exceptions to this guideline are file names, which you may want to share among a set of I/O routines. When using global variables, declare them at the lowest level that provides the necessary sharing; do not simply declare them in the outer program.

Flat vs. Hierarchical program Nesting
There are two strategies for organizing nested programs. You can use a "flat" approach, in which all nested programs are declared immediately within the outer program. Or, you can use a "hierarchical" approach in which some nested programs are declared within other nested programs, and each nested program is declared at the lowest level that permits its potential callers to reference it.

The flat approach is similar to the way paragraphs are used. It’s also similar to C’s organization of functions. This approach is simpler to manage, but provides less control over the visibility of routine names within a single source member. With well-chosen nested program names, however, name collisions are not usually a serious problem. With the flat approach, all global variables are visible throughout the source member, so you must use at least a partially hierarchical approach if you need to declare shared variables that have a more limited scope than member-wide.

The hierachical approach is similar to the way Pascal’s organization of procedures. It permits a high degree of control over the scope of both variables and nested program names.

Ordering Nested Programs and Paragraphs
Within the flat approach to ILE Cobol nested programs, or with Cobol/400 paragraphs, order the nested programs or paragraphs alphabetically within related groups. Suggested groups are:

  • Ad hoc routines that are a result of decomposing the program into smaller functional pieces
  • Common utility routines
  • I/O routines (use the file name as a prefix, so routines for the same file are grouped together)
  • Error handling routines Use a comment header, such as the following, for each group:

* = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
* I/O routines (alphabetically).

Do not try to order nested programs or paragraphs based on where Calls or Performs appear (i.e., attempting to put a nested program after the nested program in which it’s called). There’s no simple method of sequencing nested routines based on a source file’s static invocation structure, and this approach can make locating a routine difficult.


Environment Division
The Environment Division is optional and should be omitted unless you are defining Special-Names (e.g., Linkage Type) or File-Control entries.

Select statement
For Select statements, put each clause on a separate line:

Select CustFile 
       Assign       Database-Cust  
       Organization Indexed 
       Access       Dynamic 
       Record Key   Externally-Described-Key 
       File Status  CustFileSts.

Use a consistent suffix (e.g., FileSts) for the File Status variable name.

Optionally, you can simplify file declarations by using a set of Copy members, one for each combination of Organization and Access. The internal file name (and optionally external name) can be Replacing parameters (see section on Copy).


Working-Storage Section

Copy statements for standard declarations
Place Copy statements to include standard data types and work variable declarations at the beginning of Working-Storage. Follow these Copy’s with Copy’s for record formats:

Working-Storage.
    Copy StdDtaType.
    Copy StdWrkVar.

    Copy Dds-All-Formats of CustMast with Alias.

Order of declarations
If you have any Global declarations, group them alphabetically at the beginning of Working-Storage (after standard Copy’s). If you use External declarations (not recommended), group them alphabetically before the Global declarations. Use a comment and dashed line to set Global declarations off from local variable declarations:

* - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* Global declarations. 
 
 01  CustFileSts   Global  Like FileStatus. 
 
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
* Local declarations. 
 
  .... 

If you have no global declarations, you don’t need the "Local declarations" comment.

For scalar variables (not structures, arrays, or pointers), you can either declare them as a series of 01-level variables (do not use the vestigial 77-level), or declare them as a series of 02-level variables under one or more encompassing groups. Within each set of variables, order the declarations alphabetically.

Using 02-level declarations may save some storage (because Cobol aligns 01 declarations), which may be significant if you have lots of scalar variables. However, don’t overuse grouping as a way of collecting loosely-related variables. Instead, use a common prefix in the variable identifiers. Groups should normally be used to define record structures which are aggregates of closely-related variables that are passed between routines.

Level numbers
Increment level numbers by 1 so each item’s level number is the same as its actual level in the structure. Do not skip level numbers. For example:

01 UsrSpcData. 02 UsrSpcBuffer Pic X(32767). 02 UsrSpcHdr Redefines UsrSpcBuffer. 03 Filler Pic X(108). 03 UsrSpcInputOffset Like Bin4Type. ....

Incrementing level numbers by 1 reduces the potential for inadvertent errors in group Move and Move Corresponding operations, as well as for incorrect grouping of subordinate items. If a group item is re-structured, renumber the levels to maintain the proper level numbers for each item. For levels 02 and higher, use a single space after the level number.

Indentation
Begin level 02 in column 12 (as required by Cobol). Indent each lower level by 3 columns, so the new level number begins under the first letter of the previous level’s identifier.

Alternative style: Put 2 spaces between the level number and the identifier, and indent each level by 4 columns.

Alignment
Align Like or Pic and other declaration clauses.

Filler keyword
Use the Filler keyword for elementary items that are just placeholders. Don’t define any items without a variable name.

Order of keywords
Use a consistent order for keywords, and don’t use the "noise" words is and usage. Examples of one approach follow:

01 SampleChr Pic X(10) Just Right Value "Sample". 01 SampleNbr Global Pic S9(9) Binary Value 12. 01 TableName. 02 TableMax Like Integer Value 100. 02 TableEntry Like Str50 Occurs 100.

This approach has the following rules:

  • If Global is specified, it immediately follows the identifier (all Global variable declarations should be grouped together).
  • The next item is either a Like reference or a Pic phrase.
  • If a usage phrase is specified, it immediately follows the Pic phrase (the optional usage keyword itself is not coded).
  • Other phrases are put on successive lines, and aligned with the Like or Pic phrase. Spacing is adjusted to create a table-like layout in the declarations.
  • If a Value phrase is specified, it’s the last phrase in the declaration.
Like keyword and standard "data types"
Simplify declarations and reduce truncation and type-mismatch errors by using a small number of standard data types. Place standard "type" declarations in a Copy member such as the following:

01  StdDataTypes. 
    02 Ptr     Global Pointer. 
    02 Integer Global Pic S9(9) Binary. 
    02 Char    Global Pic  X(1). 
    02 Str50   Global Pic  X(50). 
    02 Bool    Global Pic  1. 
    ... 

Use a Copy to include these definitions at the beginning of Working-Storage.

Working-Storage Section. 
    Copy StdDtaType. 

Following this Copy, add other type declarations, if needed for variables in the specific program:

01 DataTypes. 02 Str1000 Global Pic X(1000).

Then use the Like keyword for subsequent declarations:

Working-Storage Section. ... 01 RcdCnt Like Integer.

You can use Like to refer to previous declarations that are "type" declarations or to fields in database records that are Copy’d into the program.

Note that you don’t need to declare a "data type" variable for a unique declaration. However, most program variables are either of some general type (e.g., integer), or they’re defined in or based on a field in a file. Thus, you should have very few Pic declarations in most programs.

Condition names (88-level)
Condition names provide a useful form of enumerations. For Boolean (and indicator) values, use the following structure:

01 ApiFldOverridesCnd Like Bool. 88 ApiFldOverrides Value TrueVal. 88 NotApiFldOverrides Value FalseVal.

Note the following conventions:

  • All three identifiers are formed from the same root (e.g., ApiFldOverrides).
  • The variable has Cnd as a suffix and uses the standard data type Bool.
  • The "true" condition name is the base name (no prefix or suffix) and uses the Replace "macro" (see below) TrueVal for it's value.
  • The "false" condition name has Not as a prefix and uses the Replace "macro" (see below) FalseVal for it's value.

You can follow other consistent conventions for forming condition names, but don’t use a mixture of styles. For example, don't use NotApiFldOverrides (Not prefix) in one case and ApiRtnErrorOff (Off suffix) in another case. Optionally, you can create a Copy member with a single Replacing parameter to define Boolean conditions (see below).

With these conventions, you can use the following easily comprehended assignments and tests:

Set ApiFldOverrides to True 
Set NotApiFldOverrides to True 
If ApiFldOverrides ...
If NotApiFldOverrides ...
	or 
If Not ApiFldOverrides ...

Note how the Not prefix convention lets you use either of two similar approaches to testing for the false condition. Use one or the other of these styles consistently.

You can also use simple assignment of one condition to another (where sensible):

Move AllowCreditIncreaseCnd to  
     ShowCreditLimitCnd

For non-Boolean enumerated conditions, use a style like the following:

01  ApiFldUsageEnum         Like  Char.
88 ApiFldUsageIsInput Value "I". 88 ApiFldUsageIsOutput Value "O". 88 ApiFldUsageIsInOut Value "B". 88 ApiFldUsageIsNeither Value "N".

This convention uses Enum as the suffix of the variable to distinguish it from a Boolean variable, and all values begin with the same root name as the variable and have a descriptive suffix that begins with Is. This provides readable tests like:

If ApiFldUsageIsOutput ...

Don’t use condition names for non-Boolean variables unless you can define a complete enumeration (all values can be covered by an 88-level condition name).

Initial values
Declare all pointers with initial value of Null. This enables you to use

If Ptr Not = Null
to guard pointer operations.

Do not depend on the declared initial value (the Value phrase) of a variable (e.g., a counter) as a way to initialize the variable before it’s used. Always explicitly initialize variables in the Procedure Division. This approach makes your code correct for repeated execution, whereas depending on declared initial values makes your code correct only the first time it’s Call’d or Perform’d.

For counters, sums, and other simple scalar variables, use Compute or Move to intialize a variable:

Compute OrdTot = 0

Compute CurStrPos = 1

Compute CurStrPos = StrSize

For output records, you can use the Initialize statement to move zero or spaces to fields in preparation for building a new record. Don’t use Initialize to set a counter to zero (it’s an unnecessarily indirect way to change the starting value).


Linkage Section
In the Linkage Section, observe the Working-Storage Section guidelines on declarations.

Declare program parameters first, in the same order as they appear on the Procedure Division Using clause.

Follow parameter declarations with local, based variable declarations in alpabetical order.


Procedure Division
The Procedure Division of an ILE Cobol program should have the following layout:

Procedure Division Using ... 
 BeginProc. 
 	... statements 

    Goback.

Note that with nested programs, there’s no need for sections or paragraphs (other than BeginProc). Instead of explicitly coding Goback, you can use a Replace "macro" (i.e., EndProc), as decribed below, which includes the Goback..

The Procedure Division of a Cobol/400 program should have the following layout:

 Procedure Division Using ... 
 BeginProc 
	... statements

    Goback. 
... paragraphs 

A Goback statement functions like a Stop Run when executed in the main program of a run unit, and functions like an EXIT when executed in a subprogram.

Use of the period and comma characters
Don’t use commas because they can be mistaken for periods, which effect the structure of Cobol programs.

Periods are not required after most Cobol Procedure Division statements. Follow one of two styles: either use a period after every statement, or use a period only when required, e.g., after the last statement in a program. (Note that the AS/400 debugger recognizes statements based on periods, so you may want to use periods to provide individual breakpoint targets.) With either style, use explicit scope terminators (e.g., End-If) as the primary means of specifying the program’s control structure.

Indentation
Indent a consistent number of spaces, from 2 to 8, to show nested control structures.

Use "adaptive" indentation and alignment for multi-line statements, such as Call.

If AmtDue of Order > MaxCredit Call "CreditExceeded" Using Order MaxCredit of Customer Else Call "ApproveOrder" Using Order End-If

Align scope delimiters (e.g., End-If) with the beginning of the statement (e.g., If). (Don’t indent the scope delimiter to align with the subordinate code.)

Alignment
Align parts of multi-line statements and a sequence of similar statements to provide a "columnar" or "table-like" look to your code where this aids comprehension.

For a series of Compute statements, always align the =. Also align the other operands when the alignment improves readability of the sequence of operations, as shown below:

Compute Discount = TotalCost * DiscountRate
Compute NetCost = TotalCost - Discount
Compute SalesTax = NetCost * SalesTaxRate
Compute AmtDue = NetCost + SalesTax

Here are some more examples of good alignment:

Move CreditLimit of CustRcd 
  to CreditLimit of CustDsp 
 
String "FieldList for " Delimited by Size 
       FileLib          Delimited by Space 
       "/"              Delimited by Size 
       FileName         Delimited by Space 
       ":"              Delimited by Size 
       RcdFmt           Delimited by Space 
  into UsrSpcText 
End-String 
 
Search MapTableEntry 
  When MapKey = KeyIn 
       Move MapValue(MapIdx) to ValueOut 
       Set  KeyFound         to True 
End-Search

Note how the Move, String, and Search examples use right-alignment for the to, into, and When keywords. This type of alignment avoids overly deep indentation and keeps the operands in an easy-to-read columnar layout.

Symbols vs. reserved words
Generally, mathematical symbols convey the meaning of code more succinctly than equivalent reserved words.

Use Instead of
< Less than
<= Less than Or Equal
= Equal
>= Greater than Or Equal
> Greater than
Not = Not Equal

Arithmetic assignment
Whenever possible, use Compute rather than Move, Add, Subtract, Multiply, and Divide, for arithmetic assignments. This simplifies code and makes it more consistent. (Note in particular how the Multiply...by statement stores the result in the second operand (following the by), which is counter-intuitive and error-prone.)

Use Instead of
Compute Num1 = Num2 Move Num2 to Num1
Compute Num1 = Num1 + Num2 Add Num2 to Num1
Compute Num1 = Num1 - Num2 Subtract Num2 from Num1
Compute Num1 = Num1 * Num2 Multiply Num2 by Num1
Compute Num1 = Num1 / Num2 Divide Num2 into Num1

There are a two cases where the specific arithmetic statement (or Move) may be required:

  • When Corresponding is used
  • When Remainder is used with Divide
Compound logical expressions
When you have a compound logical expression, unless it is very short, place each part of the expression on a separate line. Place the And and Or logical operators at the end of the line. Align the operands, the comparison operators, and the And and Or keywords. Optionally, enclose each part in parentheses. Use one blank line after the last part of the condition.

If ( AmtDue    >     CreditCheckMinAmtDue ) And 
   ( AmtDue    >     CreditLimit          ) And 
   ( CreditSts Not = CreditStsGood        ) 
 
    Call "CreditExceeded" Using ... 
         ... 
End-If. 

Never use Cobol's "abbreviated" logical expressions, such as:

If AmtDue > CreditCheckMinAmtDue And CreditLimit ...

This is error-prone and unclear.

Always fully parenthesize any compound expression that has both an And and an Or. This avoids errors caused by misunderstanding the compiler's default precedence.


CODING PRACTICES

Minimize global data in ILE Cobol
In most cases, don’t share data among ILE Cobol routines by using Global or External data. Instead, pass shared data as arguments to called nested or external programs. Keeping data private reduces the possibility for accidental modification, and lets your code clearly show the interfaces between different parts of the program.

One exception to this guideline is for internal file names that you want to reference in multiple nested programs that provide I/O routines. Since Cobol doesn’t allow you to pass a file name as an argument, you must declare the file name with Global on its FD entry.


Don't use Goto's
Goto's are completely unnecessary in Cobol. Never use them.


Don't Use Next Sentence
Use Continue instead of Next Sentence. Continue is a true "no-op", whereas Next Sentence transfers control to a point determined by where the period is placed.


"Guarding" Code
"Guarding" code refers to the practice of explicitly checking to be sure your assumptions are valid before executing a piece of code.

Loops
Always test to be sure you don’t exceed the size of an array or the length of a string when you are processing a table or string in a loop.

Division by zero
Always check that a divisor is non-zero before using it.

Arithmetic overflow
Use the On Size Error phrase when there’s any chance that overflow may occur on a calculation.

I/O errors
Always check the result of an I/O operation, using either the Invalid Key or file status value.

Pointer operations
Always initialize pointers to Null and check them before use:

If Ptr Not = Null

Call results
Use On Exception to check the results of any dynamic program Call.


Modifying parameters and passing arguments by content
For additional protection against inadvertent modification of an argument passed on a Call, use the by Content phrase before any argument that corresponds to an "input-only" parameter in the called program. When you specify by Content, a copy is made of the argument, and a pointer to the copy is passed to the called program, so the called program can't inadvertently change the value in the calling program.

In a called program, you shouldn’t modify output or input-output parameters until the very end of the program (or after you’ve made a copy of all input and input-output parameters). The following example shows how problems can occur if you modify an output parameter too soon.

Called program 

* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
* Parameters. 
* Name     Type       Usage  
* -------- ---------- ------ 
* InParm    Integer    In  
* OutParm   Integer    Out 
  ... 
     OutParm = 0 
     OutParm = OutParm + InParm  
 
Calling program 

  01 TmpVar Like  Integer. 
  ... 
      Compute TmpVar = 1 
      Call "Pgm" Using TmpVar 
                       TmpVar 

In this example, the value of TmpVar after the call will be 0, not 1. The safe way to code this example is:

01 TmpResult Like Integer. ... TmpResult = 0 TmpResult = OutParm + InParm OutParm = TmpResult

This example is highly simplified to illustrate the problem and solution, but you must be very careful anytime you have a called program that has an input or input-output parameter of the same type as an output or input-output parameter, since the calling program may pass the same variable for both.


REUSABLE CODE

ILE Replace directive
ILE Cobol supports a Replace directive, which you can use to define one set of commonly-used "shorthand" terms (these are known as "macros" in some other languages). Put your Replace directive in a Copy member that you copy at the beginning of your program. Here’s a sample StdReplace Copy member:

 
  *Control NoSource 
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = 
* Copy member: StdReplace 
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
* Purpose.  
* Defines standard Replace terms. 
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
* Replacing parameters. None. 
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
* Dependencies. None. 
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
* History. 
* Version   Date    Person Purpose 
* -------  ---------  ---  -------------------------------- 
* 1.00.01  13-Jun-95  PTC  Original version 
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = 
 
 Replace 
 
 ==Proc== by == 
 Id Division. 
 Program-Id. == 
     
 ==EndProc== by == 
     Goback. 
 End Program == 
 
 ==TrueVal==  by ==B"1"== 
 ==FalseVal== by ==B"0"== 
 . 
 
  *Control Source 

The definitions of Proc and EndProc enable you to declare nested programs as follows:

Proc LookUpPart. 
    ... 
Procedure Division Using ... 
BeginProc. 
    ... 
EndProc LookUpPart. 

The StdReplace Copy member is a good place to define mnemonics for commonly used constants, e.g., TrueVal and FalseVal.


Copy members
Place re-usable code in Copy members, rather than repeating it in-line in every program. Copy members can be used for Cobol "boilerplate", declarations, and procedural statements.

Each Copy member should have a header as shown in the following example:

 
  *Control NoSource 
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = 
* Copy member: MapTable 
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
* Purpose.  
* Declare a table that maps a key to a value. 
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
* Replacing parameters. 
* Name          Rqd Comment 
* ------------- --- -------- -------------------------- 
* TableName     Yes Group item identifier 
* TableSize     Yes Max elements in table 
* KeyType       Yes Key data type, e.g., Integer 
*                   This is referenced by Like clause 
* ValueType     Yes Value data type, e.g., Str50 
*                   This is referenced by Like clause 
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
* Dependencies. 
* 1. StdDtaType Copy member. 
* 2. Accessed by MapKeyToValue nested program, which is  
*    defined in MapProc Copy member. 
* 3. Must be initialized before accessing. 
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
* History. 
* Version   Date    Person Purpose 
* -------  ---------  ---  -------------------------------- 
* 1.00.01  13-Jun-95  PTC  Original version 
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = 
  *Control Source 

 01  TableName. 
    02 TableMax    Like   Integer 
                   Value  TableSize. 
    02 TableEntry  Occurs TableSize. 
       03 MapKey   Like   KeyType. 
       03 MapValue Like   ValueType.

The *Control Source/NoSource eliminates the header comments from the compiler source listing (regardless of whether or not you specify Supress on the Copy statement).

When you use the Replacing phrase on a Copy directive, align the from and to strings, as shown below:

Copy MapTable Suppress Replacing  
     TableName by JobIdToTitleMap 
     TableSize by 1000 
     KeyType   by JobId 
     ValueType by JobTitle. 

The optional Suppress keyword excludes the contents of the Copy member from the compiler listing. Generally, you shouldn’t need to see the generated code once you have a well-tested Copy member.

Note that ILE Cobol allows nested Copy members (a Copy member can contain a Copy directive), but you cannot use the Replacing phrase with nested Copy’s.

Replacing part of an identifier
When you want to replace just part of an identifier, you can use a "trick" to circumvent an apparent limitation of Cobol's Copy. The following example shows how to define a Select template in a Copy member so only one value has to be replaced to form the unique identifiers:

Select (File)File 
       Assign       Database-(File)  
       Organization Indexed 
       Access       Dynamic 
       Record Key   Externally-Described-Key 
       File Status  (File)FileStatus.
 ... 
Copy SelIdxDyn Replacing 
     ==(File)== by Cust. 

Cobol recognizes the () as delimiters when it preprocesses the source and matches the three tokens in the ==(File)== pseudo text in the Replacing clause of the Copy.

The resulting code is:

Select CustFile 
       Assign       Database-Cust  
       Organization Indexed 
       Access       Dynamic 
       Record Key   Externally-Described-Key 
       File Status  CustFileStatus.

ILE Copy with file formats
In ILE Cobol, use the new Alias and Substitute keywords, instead of the Cobol/400 DD-, DDS-, DDR-, and DDSR- variations. Use the I-Fields, Indicators, and other new keywords instead of the -I, -Indicator, and other Cobol/400 suffixes.


ADDITIONAL NOTES

Recursion
Recursive program calls (a program calls itself directly or indirectly) are supported by Cobol/400, but not by ILE Cobol.


Default linkage type in ILE Cobol
The default linkage for ILE Cobol program calls is Program, which generates dynamic program calls. You can override this default to Procedure on the CrtCblMod command or by using a Process LinkPrc directive at the beginning of the source member. You can override specific Call’s with the Linkage Type clause of the Special-Names paragraph or with an explicit Linkage Type phrase on a Call. Procedure linkage generates bound procedure calls.

If you plan to use bound procedure calls for most ILE Cobol applications, you may want to consider changing the CrtCblMod command default to LinkLit(*Prc) or placing a Process LinkPrc directive at the beginning of most source members. Then code a Linkage Type Program clause in the Special-Names paragraph to explcitly specify any dynamic program calls. The advantage to this approach is that the CrtPgm command will catch unresolved bound procedure calls during the bind process. If you forget to designate a dynamic program call, you’ll get an error during the bind step, which is easy to correct. On the other hand, when Linkage Type Program is the default, if you forget to designate a bound procedure call, the error won’t be caught until the Call executes and the system can’t locate a program with the name on the Call.




  Sponsored Links   Featured Links


Penton Technology Media
Connected Home | SQL Server Magazine | Windows IT Pro
Report Bugs | Contact Us | Comments/Suggestions | Terms of Use | Privacy Statement | Trademarks
See Membership Levels | Subscribe | Free E-mail Newsletters | Free RSS Feeds | My Profile | Upgrade Now | Renew Now

© 2010 Penton Media, Inc.
Penton Media
System i is a trademark of International Business Machines Corporation and is used by Penton Media, Inc., under license. SystemiNetwork.com is published independently of International Business Machines Corporation, which is not responsible in any way for the content. Penton Media, Inc., is solely responsible for the editorial content and control of the System iNetwork.