Tips & Tricks

From EDM2
Jump to: navigation, search

By Richard K. Goran

Spaces, Concatenation and Alignment

I have the opportunity on a regular basis of looking at different personal styles of writing REXX programs. Computers are wonderful machines that don't depend on aesthetics; however, my tired, old eyes do. One of the first things I invariably have to do, to assist someone in determining why a program doesn't work, is to reformat portions of the program so that I can attempt to follow the flow of the program. The suggestions that I have included are recommendations that I generally offer to people to make it easier for them to be more productive when writing REXX programs. Most of these suggestions don't result in any extra burden when a program is initially being written and they can save many hours of debugging and maintaining the program.

Fragments 1 and 2 include the same working REXX program. This program creates a Workplace Shell object on the Desktop that can be used to launch the REXXTRY program in a windowed OS/2 session. The sequence numbers at the right of each line of code, even though they are valid REXX comments, are intended solely for the convenience of making reference to the instruction in this column. Reference to specific line numbers is shown below in brackets.

The program illustrated in Fragments 1 and 2 serves to demonstrate a number of points related to REXX coding style. The use of one or more space characters in REXX, except within literals, is left completely to the discretion of the user. Wherever a single space is permitted, more than one space can be used. Proper use of spacing and alignment of the source instructions in a REXX program make the program much easier for you or someone else to read and maintain.

It is difficult to see in Fragment 1 that the SysCreateObject() function has 5 parameters and that the fourth parameter, the Workplace Shell setup string, contains a semicolon delimited list of key word / value pairs [0003-0005]. In Fragment 2 the values used as the parameters being passed to the SysCreateObject() function are first assigned to variables [0002-0011]. The variable names selected are consistent with the names used in the definition of the function in most references where the definition appears and the simple technique of aligning the equal signs (=) provides much more readable code.

The style used for setup in Fragment 2 [0006-0011] may seem a little unusual at first. There are a number of different techniques in place here. First, and probably most obvious, is that the concatenation symbol (||) is aligned on each line. Notice also that the number of blanks or spaces between the literal portion of the line and the concatenation symbol vary. Probably the most unusual line in this group is line 11. The presence of the null string (a literal with a length of zero) permits lines to be added or changed around without the need for altering the punctuation because of the change. Each of the lines has the same form because the null string provides the termination of the literal. As you can see, adding or removing a parameter from the string in Fragment 1 results in much more effort. There is also much more chance for causing a punctuation error when all of the tokens are strung together.

The difference in the use of the SysCreateObject() function (Fragment 1 [0003-0005] - Fragment 2 [0012]) illustrates using a function as an expression versus explicitly calling a function. Though I have written about this in previous columns, it bears repeating since it is one of the most frequently asked questions I hear. Any REXX function can be used either way. However, most people get acclimated to one form and never use the other. It really makes sense to use both. When it is intuitive to use the function as an expression, use it that way. If, on the other hand, it makes more sense to see the function explicitly called, don't hesitate to write it that way. A good example of the two different techniques involves the LINEIN() and LINEOUT() functions. Using

input_line = LINEIN( file_name )

makes more sense than calling LINEIN() and then assigning RESULT to input_line. By the same token,

call LINEOUT( file_name, output_line )

makes more sense than

rc = LINEOUT( file_name, output_line )

particularly if the value returned by the LINEOUT() function is not going to be checked (which is generally the case). I know that C programmers will argue this point with me. Conversely, COBOL programmers who are using REXX will provide the best support for use of the explicit call. That's part of the beauty of REXX - have it your way. I am not saying you must use the techniques that I am describing here, just that the options are available to you.

Fragment 1

 /* 9507FG01.CMD - */                                              /* 0001 */
 title='Sample REXXTRY Object'                                     /* 0002 */
 rc=SysCreateObject('WPProgram',title,'<WP_DESKTOP>',,             /* 0003 */
    'EXENAME=REXXTRY.CMD;PROGTYPE=WINDOWABLEVIO;PARAMETERS=%;'||,  /* 0004 */
    'OBJECT_ID=<REXXTRY_TEST>;REPLACE')                            /* 0005 */
 if rc = 1 then                                                    /* 0006 */
    do                                                             /* 0007 */
       say title||' created'                                       /* 0008 */
    end                                                            /* 0008 */
 else                                                              /* 0009 */
    do
       say 'Unable to create '||title                              /* 0010 */
    end                                                            /* 0011 */

Fragment 2

 /* 9507FG02.CMD - */                                              /* 0001 */
 option   = 'REPLACE'                                              /* 0002 */
 class    = 'WPProgram'                                            /* 0003 */
 title    =  'Sample REXXTRY Object'                               /* 0004 */
 location = '<WP_DESKTOP>'                                         /* 0005 */
 setup    =,                                                       /* 0006 */
    'EXENAME=REXXTRY.CMD;'           ||,                           /* 0007 */
    'PROGTYPE=WINDOWABLEVIO;'        ||,                           /* 0008 */
    'PARAMETERS=%;'                  ||,                           /* 0009 */
    'OBJECT_ID=<REXXTRY_TEST>;'      ||,                           /* 0010 */
    ''                                                             /* 0011 */
 call SysCreateObject class, title, location, setup, option        /* 0012 */
 if RESULT = 1 then                                                /* 0013 */
    do                                                             /* 0014 */
       say title || ' created'                                     /* 0015 */
    end                                                            /* 0016 */
 else                                                              /* 0017 */
    do                                                             /* 0018 */
       say 'Unable to create ' || title                            /* 0019 */
    end                                                            /* 0020 */

IF / ELSE With DO / END

Since only one instruction is permitted following both parts of an IF / ELSE construct, it is necessary to use the DO / END pair to group multiple instructions together. I am sure that everyone who has written a REXX program of any size has written a conditional IF statement with a single instruction following it only to have to go back and add the DO / END pair when it became necessary to add a second instruction following the conditional test. By always following both the IF and ELSE keywords with a DO / END pair, you can save yourself the effort required to add them only when they are absolutely necessary. If you get into the habit of always following IF with DO / END, it will become second nature to you and you will not have to alter your thought flow by going back to add the lines.

This leads me to the placement and alignment of the DO / END pair wherever it is used in a REXX program. Another area where a considerable amount of time can be lost is in trying to find a missing END clause. It seems that the majority of REXX programs I see, particularly those written by new REXX programmers, will have the DO / END pair aligned if they are used by themselves. However, if they are used following an IF clause, the DO typically is written following the THEN token. It seems reasonable to me that the inconsistency should be eliminated by placing the DO on a line by itself, just as the END clause is, and align the DO with its corresponding END. Another benefit that can be gained by aligning the DO and END clauses is that for editors that support a REXX type macro language, in my case KEDIT from Mansfield Software, it is quite simple to create an editor macro that will cause just the DO and END lines (I include SELECT clauses too) to be displayed. The chore of locating an unpaired set of DO / END clauses then becomes a much easier task.

REXXTRY

Anyone not familiar with REXXTRY should take the opportunity to try it. It is a REXX program that allows one or more REXX instructions / expressions to be entered interactively without having to create a file containing the REXX instructions. For example, have you ever had to refer back to the REXX documentation to see what the default format of the string returned by the built-in DATE() function is. By launching REXXTRY from an OS/2 windowed or full screen command line session (or with the sample program I am using here) you can enter the instruction SAY DATE() and see the resulting string. You can also use REXXTRY to run a short, one-time REXX program. Successive lines can be entered just as you would create them in a REXX program.

DO / End Repetitors

Similar to the idea of aligning the DO / END pair is the technique of including the repetitor (admittedly, a word I have never seen used outside of the context of REXX) on the END clause. Frequently programmers append comments to an END clause line indicating which DO loop is being terminated. By including the repetitor variable name as part of the END clause, the same result is obtained along with the added benefit of having the REXX interpreter produce an error message if nested DO / END pairs are not properly defined. (This is unfortunately not true with REXX on OS/2 2.1 because of a defect in the interpreter).

As long as I am discussing the use of the repetitor on the END clause, I would like to ask the rhetorical question of why a repetitor, index, subscript, or whatever term you would like to use, is always the letter I for a first choice followed by the letter j. The answer probably lies in the use of integer values in the FORTRAN compilers of the early 1960s. Integers had to be defined beginning with a letter of i through m if my memory is correct (please hold the E-mail). Therefore, the use of the letters i and j have just evolved over time. If you break the habit and begin using repetitors which are more relevant to the list or array that they are used to reference, you can save yourself a lot of grief when you find that you have unintentionally used the same variable in two different places at the same time - though I'm sure that's never happened to you.

Fragment 3

    do index_1 = 1 to 10      /* 0001 */
       do index_2 = 20 to 25  /* 0002 */
          ...
       end index_2            /* 0004 */
    end index_1               /* 0005 */

Compound variables - Arrays

Seemingly, one might suspect REXX of being colorblind. Fragment 4 contains three lines which you can test using REXXTRY. Launch REXXTRY in an OS/2 windowed or full screen session, enter the three lines from Fragment 4 and you might be surprised at the results. Though you probably expected the result of the SAY instruction to be gray, you probably got a darker answer. This is not a defect in REXX; rather, it is REXX "gotcha". When you reference a compound variable in REXX, only the stem (that portion of the name up to and including the first period) is converted to upper case. The tail (that portion of the name following the first period) is processed in the same case it was written in. Therefore, if you could look into the REXX variable pool (the storage where REXX variables are kept), you would see that while "SHADE" was assigned the value of "gray", the variable "COLOR.shade" was assigned the value of "black" in the first line that you entered.

Fragment 4

color. = 'black'
shade  = 'gray'
say color.shade

On The Road Again

In next months column I will report on my adventures at the 1995 REXX Symposium being sponsored by the Stanford Linear Accelerator Center. Also, I hope to meet a lot of OS/2 enthusiast at the OS/2 World Conference & Exposition in Boston from July 18 - 21 where I will be doing presentations along with my colleague, David Moskowitz. I will be speaking on REXX and Warp.