Style Guide for Rexx

Introduction.

This is a style guide for the Rexx programming language,
primarily aimed at Rexx in the OS/390 environment, but
application to Rexx on other platforms, and to variants of Rexx
such as NetRexx. Many of the points here have been raised at some
time on the TSO-REXX
listserv mailing list.

Using Comments.

Correctly commenting code is one of the most useful things you
can do when writing code. Most style problems can be repaired
with a formatter, but useful comments cannot be conjured out of
thin air.

Comments come in two flavours, winged
and boxed:

/* This is a winged comment */
/**************************************\
* This is a *
* boxed comment *
\**************************************/

Boxed comments should be reserved to highlight major
structural elements, such as the start of procedures. See the Calculate_Factorial() example.
Winged comments are useful for describing the purpose of the
statement(s) immediatly adjacent to the comment.

It may also be worth borrowing from the Javadoc
idea. Comments enclosed by /** ... */ may be
read by a suitable program, and the text within associated with
the immediately following Rexx statement, and used to automate
the creation of external documentation for the program.

The use of asterisks (*) as the box border can
make the comment too heavy. Consider using minus (-) or another
lighter character instead. Also, don't worry about closing the
right hand vertical, as realigning the border after text
insertion is a tedious affair. You may have a Rexx reformatter
that deals with this problem.

A common use of boxed comments is for an informative
header at the start of the exec. Here is an example:

/* Rexx ----------------------------------------- AUTOTOOL
<A one line description for use by an indexing tool>
-------------------------------------------------------
Copyright: <You may want a copyright notice>
Change History:
<yy/mmm/dd Userid VersId Description>
Description:
<A long description of the purpose of the exec.
Include invocation arguments, examples of call
syntax, returned values, etc.>
---------------------------------------------- MEMNAME */

It is extremely useful to have a tool to handle the creation
of a standard header, and to handle change history information.
Note the marker in the top right of the box that may be used by a
tool to spot whether this is a standard header or not.

An indexing tool that reads the short description and change
history is also useful, so make sure the header format supplies
information that would be useful to an indexing tool.

In the change history, put the most recent changes at the top,
so they are visible when the exec is opened. It may also be
useful to have a version id to mark changed code in small
comments later on.

It may not be worth putting some types of information in the
header, particularly data that ages poorly. Dataset names and
pointers to external information are particularly prone to
becoming incorrect.

Using 'content free' phrases such as 'We call
this Rexx program to get a returned value to the caller
consisting of....'. This also applies to
function names, E.g. In Perform_Update_Function(), the
words 'perform' and 'function' tell us nothing useful,
try Update_Table() instead.

Using Symbols and Variables.

The Rexx ANSI standard talks of Rexx 'symbols'. Here I shall
use the more familiar terms 'variable', 'variable name' and 'variable
value'.
Compound variables, stems, and tails refer to the whole and
various parts of compound variables.

Always make your variable names useful and
meaningful.

Some programmers use a form of Hungarian
Notation to show when a variable is boolean, a loop index,
numeric, etc. This may make your variable names not so easy to
read, and has other dissadvantages.

Consider using i, j, k, etc as loop control variables. There
are advantages and disadvantages to this: Faster
execution speed. Single character variable names show a
performance improvment (My simple test measured nearly 10%
improvement). Compound
variables names are shorter and less likely to make long
statements cross onto multiple lines. You have
to rely on the stem name to indicate the meaning of the data in
the compound variable. E.g. Consider compound var names record.i
and score.teamIndex.eventIndex . It is obvious
that i is the record number, but would it be
obvious from score.i.j that i
is the team and j is the event?

Global variables can be handled in the following
way:
First, in the opening section of your Exec, set a variable named
global whose contents are the stem names of your global variables:

global = 'gl.' /* set a list of stems that are global variables */
gl.testVar = 'This is a sample global variable.'

Do use the procedure keyword
with every defined procedure. This hides the procedure's internal
variables from the caller, and allows the use of i,j and
k as loop control variables throughout your
program without any side effects, as in this example:

This common problem occurs when a variable is
assigned a value, and the variable is then used in a compound
variable:

stem.salutation = 'HELLO!' /* literally stem.SALUTATION = 'HELLO!' */

/* Output the intended result */
say stem.salutation /* ==> HELLO! */

/* Set the tail of the compound variable to have a value */
salutation = 'GOODBYE'

/* Now show an often unintended result */
say stem.salutation /* ==> STEM.GOODBYE */
exit

If you don't want this effect, you must use unassigned
variable names in the compound variable. You could use non-alphanumeric
characters such as ! or ? to
prefix the variable name when its used in the compund tail, or
use a numeric prefix, as symbols starting with numerics are, by
definition, constants.

testVar = 'HELLO'
stem.0testVar = testVar
stem.!testVar = testVar

Numeric prefixes are better for code
portability.
The ISPF editor picks special characters (such as !) up in HILTE
mode.
Non-alphanumeric characters may not be portable to foreign EBCDIC
character sets, and are not portable to some Rexx extentions,
such as Object-Oriented Rexx.

Large blocks of variable assignations can be
split into columns, with variable names, variable values, and
winged comments:

Case, Indentation and Block Structure.

Here we discuss the case we could use for syntactic elements,
what indentation is appropriate, and how we could break blocks of
statements up.

But first, What are we trying to achieve, and how do we go
about achieving our aim.

We are trying to increase the readability of the
code.

We can improve readability by using techniques to
differentiate and highlight elements within a statement, show
relationships between statements, and lay out the code so that
these relationships and statement elements are visible to the
reader.

The target audience should be anyone likely to need to change
your code in the future, including yourself. Don't assume any
knowledge of the inner workings of your code, it may be some
years before you revisit it. A reasonable knowledge of Rexx may
be assumed, even though the reader may be new to the language.
Everyone has to learn something sometime.

Here are some elements that go to make up
statements:

Keywords such as IF, SELECT and CALL.

Variable names such as USERNAME, or USERCARDNUMBER.

Major Blocks such as DOcount
- END, SELECT - END
and label: PROCEDURE - RETURN.

Contitional Blocks such as IF cond THEN DO - END
and WHEN cond THEN DO - END.

Natural Blocks of statements that together perform a
particular function.

Builtin function names such as POS(), D2B(),
and TRANSLATE().

Implementation supplied builtin external functions such
as MVSVAR() and LISTDSI().

User supplied external functions such as MYFUNC().

Internal functions such as MYINTERNALFUNCTION().

Commands to external environments such as ADDRESS
TSO "LISTCAT"

Comments.

Literal text.

Here are some techniques to improve readability
using case:

UPPER, lower and Mixed
case.

Concatenated words with case differentiation such as ConcatenatedWords.

Underscore separated words such as underscore_separated_words.

Other techniques to improve readability are:.

Indenting blocks or statements within
blocks.

Aligning the start of a block with its
end.

Splittingstatements across
more than one line.

Inserting blank lines between blocks.

Using boxed comments before major
structural items.

We are also concerned with breaking up the code
into manageable and reusable units. We can do this by using
procedures, and by surrounding blocks of statements that perform
a task with white space.

Constraints and Influences on Style Guide
Rules

There are some facts about the programming environment that
influence what techniques may be most effective.

External function names are related to file
names in a file system, or member names in partitioned datasets.
MYFUNC() may refer to PDS 'user.EXEC(MYFUNC)' or file name MYFUNC.REXX,
depending on your Rexx environment. By using upper case for
external function names, you avoid having to quote function names
to maintain lower case in the file name. You then, of course,
must use upper case in the file names of external functions.

Screen size influences how much code can be on
the screen at one time. Using too many blank lines or splitting
statements over multiple lines can move code out of the viewable
area. Techniques that disperse statements over several lines work
well on window based systems that allow the viewable area to be
adjusted, but are restricted by the fixed terminal size of 3270
systems. The same is true for boxed comments.

Some editors are Rexx aware and can hilite Rexx code to show keywords,
quoted strings and other elements. A Rexx aware editor such as
ISPF EDIT with the HILITE utility, can for instance turn all
keywords red, and all comments blue. There's no point in using
upper case to highlight keywords, when the editor can pick them
out with colour.

Keywords appear in isolation in the text, and
also tend to appear at the start of statements, so they stand out
without any help from the programmer.

Function and variable names tend to appear close
together in code. We need to be able to differentiate variable
names from function names, and also differentiate between the
various flavours of function; internal, external, builtin, etc.
For example:

Literals should be quoted, unless you can be
sure that the literal hasn't been used as a symbol elsewhere.

Commands being passed to external environments
often have single quotes in them. E.g. address TSO "ALLOC
FI(TEST) DA('MY.DATASET') SHR REUSE".

NetRexx (and presumably other close relatives of
Rexx) are sufficiently different from Rexx to make consideration
of styles that apply to NetRexx irrelevant. Having said that
NetRexx is a case insensitive language, and most style rules
should apply to it.

An Example Set of Style Rules.

Here is programming style that meets the above criteria in
varying degrees. It tries to keep the code dense for 3270
displays, and relies heavily on a highlighting editor to
differentiate between keywords, quoted text, variable names, and
other statement elements.

Rules specifically related to case:

Keywords in lower case. Let the editor highlight them. E.g.
select do if end

Variable names starting in lower case with the initial
letters of concatenated words in upper case. This
differentiates them from function names, which will all
start with an upper case character. E.g. myName
counter fishType

Internal function names with the initial letters of
individual words in upper case. Optionally, you can also
use underscore separators E.g. MyFunction()
MyOtherFunction() Your_Function()

Always quote literal strings. Never rely on the string
not being used as a variable name. E.g. myVar = 'test'.
Never use myVar = test

Use variables for quotes if building strings containing
both single and double quotes to avoid the opening and
closing becoming confusing: E.g. myVar = dq||sq||'test'||sq||dq
rather than myVar = '"'"'test'"'"'

Rules for procedures:

Use the procedure keyword wherever
possible, to avoid collisions between variable names in
different procedures. E.g. My_Function: procedure
expose (global).

Use commas and not spaces to delimit your
arguments. See the Calculate_Factorial
demo for an example.

Use a comment in the style of javaDoc before each
procedure, so that possible future 'rexxDoc'
implementations will be able to pick up your comments.
See the LZ78 demo for an
example.

Rules for comments:

Use a standard machine readable header to document a
synopsis of the exec, change information, and any other
useful information. Create a tool to manage and extract
useful information from these headers.

Aim your comments at a reasonably competent programmer
who is not familiar with your exec.

Comment what end statements are ending. E.g. end
/* select */.

Indicate what variables are to be used for when they are
first assigned. See the example in an earlier chapter.

Use a boxed comment to mark where procedures begin. See
the LZ78 demo for an example.

Other miscellaneous rules:

Use i, k, j, etc. as loop control
variables. This can make your code run faster, and helps
keep statements short..

Indent do, select, and
other similar structures with between 1 and 3 spaces.

An aside on following a ruleset:
One important comment that must be made, is that it is not so
important that one set of rules is followed in all cases, but it
is important that a set of rules is followed consistently in a
piece of work. Leaping from one style to another mid-procedure
can hinder a readers understanding of the program.

Style Example

Here's an example program, with two similar
internal functions present, in two slightly different styles. The
compress function follows the above example set of guidelines.
The uncompress function follows a second consistent set of
guidelines. Note how the uncompress function is easier to read
when no colour highlighting is used. Copy the program into a Rexx
sensitive editor, and the compress function becomes easier to
read.

/** ================================================================
Demo LZ78 compressor. In reality, the output would be formatted
to take up less space then the original, E,g, in 9bit bytes,
with the high order bit set on when the char is a dictionary
reference, or something.
Oh, and it doesnt like spaces, cos I use WORDPOS() to search
the dictionary.
----------------------------------------------------------------- */
Compress_LZ78: procedure
parse arg inString
/* Initialise the dictionary, output string, etc */
outString = '' /* output string */
d = '' /* Dictionary. Blank delimited 'words' */
w = '' /* Last used phrase */
outCode = '' /* Our current dictionary reference for this phrase */
/* For every char in the input string, output the char, or a
dictionary reference */
do i = 1 to LENGTH(inString)
/* Get next input char, and make phrase */
thisChar = SUBSTR(inString,i,1)
thisPhrase = w||thisChar
/* If the new phrase is in the dictionary, queue up this dictionary
reference to go into the output string */
if WORDPOS(thisPhrase,d) > 0 then do
if outCode = '' then outString = LEFT(outString,LENGTH(outString)-1)
outCode = '<'WORDPOS(thisPhrase,d)'>'
w = thisPhrase
end
/* If the new phrase wasnt in the dictionary, output it */
else do
outString = outString||outCode||thisChar
outCode = ''
d = d thisPhrase
w = thisChar
end
end
/* return the compressed string */
return outString||outCode

Spot the line that is longer than 80 characters. In ISPF it
should be wrapped in whatever way is consistent with your
treatment if IF THEN DO constructs:

if outCode = '' then
outString = LEFT(outString,LENGTH(outString)-1)

Enforcing Programming Standards.

There's little point in setting guidelines for writing
programs if they're going to be ignored. You have to make people
want to follow your guidelines.

Here are some ways of encouraging the use of
programming standards:

Allow individuals to follow the guidelines as they see
fit.

Use management control.

Automate the process by supplying easy to use tools to
manage internal documentation and format code.

Relying on individuals can fail if lazy or naive programmers
fail to follow guidelines. It does allow creative and expert
programmers the freedom to use systems they are comfortable and
productive with.

Management control is particularly suited to organizations
that have a Quality Assurance program.

A tools led approach can be combined with other approaches.
There is some development effort to get tools into place, and the
tools must be flexible and fit for their purpose. Generally, a
handful of ISPF EDIT macros to perform various formatting tasks
will suffice.

Concatenating Strings.

There are two schools of thought on whether to explicitly use
the string concatenation operator ( || ) or not.

There are some good reasons to use concatenation operators,
and some good reasons not to.

Points for the use of the concatenation operator:

Programmers supporting your code often do not use Rexx
regularly.

Component elements of the string will be explicit to the
reader.

Unexpected spaces can creep in when ommitting
concatenation operators.

Points for ommiting the operator whenever
convenient:

Less is more. The resulting code is tidier.

The Hilite function of the editor makes the component
elements clear enough anyway.

There is less chance of the statement overrunning onto
the next line.

You don't have to explicitly include spaces inserted
between the strings. E.g. var1 || ' ' || var2versusvar1 var2

Some Other Dos and Don'ts

Don't use too many levels of indirection.
For instance, there is often little use in storing ISPF panel
names in variables when address ISPEXEC "DISPLAY PANEL(MYPANEL)"
pinpoints exactly where in your exec that panel MYPANEL is
actually used. Having functions call functions that call
functions can equally make debugging an exec very tiresome indeed,
so make sure that you comment what is going on if you do have to
create a complicated structure.

Do make sure abstracted procedures allow full
access to the underlying data.
For instance, say you put a nice shell exec around TSO LISTCAT.
Your shell program should be able to control all arguments for
LISTCAT, and return any information returned by LISTCAT to the
caller. Otherwise you may have to create a new interface for your
old shell if new LISTCAT data is needed. Adding new functionality
to old code creates backwards compatibility problems, and
maintenance problems as the code becomes 'hairy' from unnecessary
revisions.

Don't hide information in variables set far away
from where they are used.
Its one thing to have a nice block of commented assignations near
the top of an exec or procedure, but its another thing entirely
to place assignation statements in obscure parts of the exec, far
away from where they will actually be used.

Do end all internal procedures with an
unconditional RETURN.
This makes it clear to the reader when a procedure ends and may
help code formatting tools to spot the end of internal procedures.

Send Feedback

Please drop me a line, and tell
me about any improvements you think could be made to this page.