As we start this new year, we need to evaluate some of our RPG IV programming practices. We need to examine their viability as we move forward toward creating compelling new applications for the decades ahead.
While most programs are fundamentally sound, I see a large number of programmers writing routines that work for the moment, but not much more. Their code works as designed until some quirk pops up, and then boom!
Why do people write code that they know won't work under even the most basic of situations? What happens if a blank or zero is sent to a routine that is set up to process the letters a, b, and c? Does it react correctly or blow up? How much additional code is going to be required to allow that same routine to also accept the letters d, e, and f and the numbers 0, 1, and 2?
Time and time again, I've worked with existing code to merely add a simple option here or there. Too often, the code is so poorly written that it effectively takes a rewrite of the logic or a complete isolation of the new code to make these changes. This code often consists of hard-coded routines and values that encapsulate the original requirements only. Essentially, there is no way that anything else is getting in there without a fight. The user need only enter a wrong value and boom! It seems like programmers are protecting the code against future changes and programmers instead of preventing user errors and encouraging future programming enhancements.
This brings me to my 2005 list of the top 10 things most often done wrong in RPG IV. The list has changed since I last ran it roughly two years ago. This time, RPG II and RPG III programming habits seem to dominate the list. Some are just old habits that are apparently difficult to give up, while others are based on the newness of a feature or function and our lack of experience using it. I'll tell you what you shouldn't be doing as well as what you should be doing.
1. The B Data Type
This is a huge pet peeve of mine. Not only do I see from and to columns being specified, but I see the B data type being used pervasively. I suppose there are two reasons for this: 1) IBM continues to ship poorly designed data structures in QSYSINC for RPG IV, including the B data type along with from- and to-column notation, and 2) old habits die hard.
The RPG IV Integer data type supplants the passé binary fields. Binary fields are among the most poorly performing data types, whereas integer fields are some of the best-performing. Whenever you get the urge to use the B data type, replace that with an I, and things will work better.
One reason people continue to use the B data type is that IBM made a design mistake (in my opinion) when it implemented the replacement for B fields; programmers are required to specify the number of digits the field holds rather than use the traditional "size" specifications.
For example, a 2-byte integer is declared as 5I0. Conventionally, integers are referred to in other languages as short, int, long, or long long.
In the IBM documentation, specifically in the API manuals, the integer data type is referred to as BIN or INT (depending on who wrote the manual).
The BIN or INT notation is typically followed by the size of the integer value in bytes, like so:
Yet in RPG IV, this data type is declared as follows:
As you can see, there is no obvious association between the RPG IV designation and the documentation. I've created the following table to better illustrate the association.
RPG IV
Designation |
C and Java
|
API Manuals
|
Alternate API
Manuals |
3I 0
|
char
|
Bin(1)
|
char
|
5I 0
|
short
|
Bin(2)
|
Int2
|
10I 0
|
int or long
|
Bin(4)
|
Int4
|
20I 0
|
long long
|
Bin(8)
|
Int8
|
One final thing: Binary fields are mapped to packed decimal under the covers. This causes a conversion whenever the field is modified. Integer fields are not mapped but are used as is. The IBM compiler development team tells me that integers are the fastest numeric variables when used for array indexes, loop counters, and subscripting.
2. *INKx Indicators for Function Keys
There's just no excuse for this. Granted, an old System/36 RPG II program may still have *INKG in it to exit the program when F7 is pressed, but please don't use these indicators anymore. If you're trying to eliminate the use of indicators 01 to 99 and thus not specifying a response indicator, then use a technique that's been around as long as or longer than the *INKx indicators. The technique is to use position 369 of the workstation's information data structure. In that position, a hexadecimal code uniquely identifies each Fn key as well as the Home, Help, Print, Rollup, Rolldown, etc. keys. For example:
/COPY FKEYS
D WSDS DS
D FKey 1A Overlay(WSDS:369)
The copy member, FKEYS, is a series of named constants originally built in 1987 for my book The Modern RPG Language. You can find it in the third edition of The Modern RPG IV Language, or you can download it from the free RPGLab Web site.
It is bad enough that people still use numeric response indicators when you could have code that reads like this:
So can we please stay away from the *INKx indicators?
3. From/To Columns on the Definition Specifications
This goes hand in hand with the old Binary field data type. Why do people try to be so contemporary in their coding style but then resort to from- and to-column notation in their Definition specifications?
Back in the 1960s and 1970s, when we had data on 80-column cards, a programmer often drew lines between the individual "fields" on the card. Since the columns were numbered, it was easy to transfer the field's from and to columns to your RPG program.
A contemporary example would be the use of the API exception/error data structure. The irony is that while APIs have been around for some 18+ years, programmers still consider them a contemporary programming practice. And yet, when they use APIs, I inevitably see the following data structure in their programs.
D dsLen 1 4B 0
D RtnLen 5 8B 0
D msgid 7 15A
D szReserved 16 16A
This is the standard API exception/error data structure. What I see wrong with this data structure is the use of from and to columns for subfield positions. Today, only length notation should be used. From- and to-column notation should never be used.
If this data structure were written correctly, it would appear as follows:
D dsLen 10I 0 Inz(%size(apiError))
D RtnLen 10I 0
D msgid 7A
D szReserved 1A
Not only is the data structure written with length notation, but the INZ keyword is used to initialize the DSLEN subfield. This allows the field to be initialized to the correct value.
4. Declaring Fields on the Calculation Specifications
When IBM was designing RPG IV, I jokingly suggested eliminating the capability to declare fields on the Calculation specification. I think if there were a fast and direct way to convert RPG III code to RPG IV and move those ad hoc fields on the Calculation specifications to Definitions, IBM would have considered it.
Granted, there is a ton of existing RPG II and RPG III code out there that contains ad hoc fields on the Calculation specification. But in new and modified code today, there is no reason to continue to use this technique.
Often, if I'm in the middle of a routine and need a new field, I insert a D spec right there in the middle of my code--in the middle of the Calc specs--and then move it up with the rest of the D specs when I have time.
5. Using INZ When Declaring Data Structures
Most of the code that has issues with decimal data errors (in both RPG III and RPG IV) could have been easily fixed with one small change. That change is using the Initialization capabilities for data structures. In RPG III, this means inserting an I in column 18 of the Data Structure specification; in RPG IV, it means using the INZ keyword on the Data Structure declaration.
It amazes me how few people use this powerful tool that was added to the language over 15 years ago. The INZ keyword causes the subfields of the data structure to be initialized based on their data type rather than initialized only to blanks. Therefore, numeric fields are initialized to zero, character fields to blanks, and other data types to the default low value.
Try it; this one small change could save you hours of recovery and/or debugging efforts.
6. Using Date Operation Codes
Why are people still coding MULT 10000.01? OK, so virtually no one used true date data type fields in their DDS/database files. I get that. RPG III doesn't support them, so the heck with it. I get it. But if we all have 8-digit zoned numeric fields that contain a date value (or something similar) in our database files, why not simply move them to true date data type fields in RPG IV, do the date math, and then move them back when we're finished? Here's an example:
D ShipDate S D
D INVDTE S 8S 0 Inz(20050201)
D SHPDTE S 8S 0
C *ISO MOVE INVDTE InvDate
C InvDate AddDur 5:*DAYS ShipDate
C *ISO MOVE ShipDate SHPDTE
The fields INVDTE and SHPDTE are typical 8-digit numeric fields. In this example, I've declared them as 8-digit zoned numeric fields, but they could have just as easily come from a database file, from a READ operation.
The MOVE opcode converts the 8-digit date field INVDTE to a true date data type field named INVDATE. Then, the ADDDUR opcode adds a duration to the date value. The result is stored in a second date field named SHIPDATE.
To convert the resulting date value back to an 8-digit numeric field, the MOVE opcode is used once again.
It is so easy to convert between numeric and date fields that it seems careless to avoid the use of date operation codes and date data types.
7. Procedures and Subprocedures
When it comes to programming in the year 2005, the use of procedures in RPG IV should be mainstream. There is no compelling reason to avoid using subprocedures. Perhaps the biggest issue is writing your first procedure. Or perhaps it is understanding the difference between a procedure and a subprocedure. The difference is simple: There is none. The term subprocedure is the official RPG IV terminology for what we in programming conventionally refer to as a procedure or function. What's the difference between a procedure and a function? A function returns a value to its caller, and a procedure does not. So if you call everything a procedure, you are technically correct.
If you need to write that first procedure to help get you started, you may want to review my article on the subject. In addition, you could go to RPG World in Las Vegas in May and attend the pre-conference workshop.
8. Hard-Coding
Hard-coding, like using magic numbers, happens a bit too frequently in RPG. Instead of using a variable or an array of elements, many programmers will hard-code a routine instead of building in flexibility. This means routines are often coded more rigidly than they need to be and then left that way.
Hard-coding a routine is usually the first pass a programmer makes when writing the routine. But then, a programmer should revisit the routine to implement it in a more flexible manner.
To make a routine more flexible, avoid the use of literals, including numbers in mathematical operations. Use fields or named constants to store values so they can be modified. If possible, build in conditional logic that allows a future programmer to easily insert or modify the code. Here's an example:
C When fKey = F3
C if CleanUp() = b_OK
C Return
C endif
C When fKey = F5
C callp Refresh()
C When fKey = F11
C callp ToggleDsp()
C endSL
If another programmer needs to add logic to handle function key 6, it is not only apparent where to do that, but also easy to do.
What makes the above routine flexible? The Select/When clause combined with calling subprocedures moves the implementation into the subprocedures and compartmentalizes it, keeping it away from the main line logic of the routine. So it becomes relatively easy to modify and change this code.
And Then There Are the Magic Numbers...
This one continues to plague the code I work with, and then I have to go back and correct it. And I see it in nearly every program I maintain.
RPG III introduced the concept of named constants, and RPG IV made it much easier to declare them. Named constants help provide documentation and identification of values in a program. For example, what does the number 12 mean in the following code segment?
D sales S 7P 2 Dim(12)
C for I = 1 to 12
C eval Total = Total + Sales(i)
C endfor
The number 12 could represent the number of months in the year, the number of sales periods, or something entirely different.
What happens when programmers need to maintain this code? How do they associate the number 12 in the FOR loop with the number of elements in the SALES array? What if there are other sections of code that also use the number 12, some that apply to this example and others that do not?
A better approach to programming is to convert that "magic number" into a named constant and then use that named constant throughout the code where applicable. For example, the following modified code would be much easier to analyze and modify, so it would cost less to maintain than hard-coded magic numbers.
D sales S 7P 2 Dim(12)
D SALES_PERIODS C Const(%elem(SALES))
C for I = 1 to SALES_PERIODS
C eval Total = Total + Sales(i)
C endfor
9. Calling Modules
It seems like more people are attempting to move to modules (*MODULE objects), which means using procedures, and this is all good. But some are simply using CRTRPGMOD to compile converted RPG III code into a module and then replacing an old CALL operation with a CALLB or CALLP to the old program name. Since the program is now a module, they are calling the module instead of a procedure name. This couldn't be further from what is desired.
Calling a module name is just wrong. Here's how it works: When you compile a source member that has no procedures in it as a module, the compiler generates a procedure for the mainline calcs with the same name as the source member. Consequently, that name is also the same as the module that was created. If you bind that module with another, and the second module has a call to the first one by name, it gives you the illusion that you're calling the module.
Granted all of this does work, but just feels wrong. It is as wrong as coding a PWRDWNSYS command in the middle of CL program. You can do it, but why the heck would you want to?
If you want to use CALLB or CALLP, use procedures and call those procedures. Don't do a half-hearted effort and lie to yourself that you're "using ILE." You're not.
10. Using Forums and Email List Services to Learn RPG IV
This is more of a political view than anything else. Many times, I'll see questions on the RPG email list services that are good solid questions. But then, the same author will ask a follow-up question and then another and another until it becomes apparent that the person posing the questions doesn't know anything about the topic.
This leads me--and others who frequent those lists and provide help--to believe that the author of the questions is actually trying to learn an entire topic through the list service rather than read a book, take a class, or attend a Web seminar on that topic.
This does two things: It clutters up the list services with questions that many people are uninterested in, and it often upsets the regulars on the list.
Granted, the lists are there for people to ask questions, and people should continue to do that. The problem, however, is very specific: When people keep probing the free advice on the list in order to try to teach themselves a new subject from start to end, they alienate the people on the list who are offering the free advice.
This activity leads to any of several different outcomes, but at the end of the day, it is just wrong to abuse this privilege.
So, if you have questions, please ask them, but if you don't know anything about a topic and want to learn, limit your questions to "where can I learn more about [subject x]?"
Bob Cozzi is a programmer/consultant, writer/author, and software developer. His popular RPG xTools add-on subprocedure library for RPG IV is fast becoming a standard with RPG developers. His book The Modern RPG Language has been the most widely used RPG programming book for more than a decade. He, along with others, speaks at and produces the highly popular RPG World conference for RPG programmers.
LATEST COMMENTS
MC Press Online