Guru: Enumerated Data Types In RPG
September 16, 2019 Ted Holt
IT has changed a lot since I entered the field several decades ago, but some things have not changed. I would read in those early days that COBOL was dead, and I read the same thing now. Yet COBOL is 60 years old and still going strong. Back then I heard RPG criticized as “Real Poor Garbage”. These days I hear it scorned as “legacy”, which I assume is supposed to mean the same thing. Yet today’s RPG is better than any of its predecessors for business programming.
RPG supposedly does not have the features of modern languages. Maybe not, but I have found that I can make RPG do what I need it to do anyway. One example is enumerated data types.
An enumerated type is a data type with a restricted set of allowable values. They are wonderful because they preclude the option of assigning an invalid value to a variable. Currently hot languages like JavaScript, C# and Python support them, but they’re not new. I used enumerated types in Pascal in my university days. To learn more about enumerated types and see some examples, visit Wikipedia.
RPG has no special syntax for enumerated types, but the existing syntax works quite well. Here’s how it’s done.
Let’s say that we have a program that calls a subprocedure to process a batch of transactions of some sort. The subprocedure can operate in one of three modes:
- It can validate the data, i.e. check the data for errors.
- It can validate the data and post the data if it passes validation.
- It can skip validation and post the data.
Such a subprocedure would probably be part of a service program, but to simplify my example, I make it an internal subprocedure.
The subprocedure returns a status code to report the result to the caller. Possible status codes are:
- The subprocedure executed without error.
- The subprocedure executed, but had to make some assumptions, and therefore generated one or more warning messages.
- The subprocedure found an error condition.
Here’s a copybook in which these data types are defined.
**free dcl-s Action_t char(2) template; dcl-c Validate 'V '; dcl-c Post 'P '; dcl-c ValidateAndPost 'VP'; dcl-s RunStatus_t packed(1) template; dcl-c SuccessfulRun 0; dcl-c RunWithWarning 1; dcl-c RunFailed 2;
I’ve defined two datatypes: Action_t and RunStatus_t. I like to end the datatype names with _t. Notice the TEMPLATE keyword on each declaration to prevent the compiler from allocating memory.
For each datatype, I’ve defined a set of allowable values as constants. Since RPG is not case-sensitive, I prefer to make constants look like any other type of identifier.
Any source member that includes this copybook can define variables and functions of these data types.
Here’s an example caller:
**free /include copybooks,DataTypes dcl-s Stat like(RunStatus_t); dcl-s BatchNumber packed(3); Stat = ProcessBatch (BatchNumber: ValidateAndPost); if Stat = RunFailed; // do something to handle the error endif; dcl-proc ProcessBatch; dcl-pi ProcessBatch like(RunStatus_t); inBatch packed(3) const; inAction like(Action_t) const; end-pi; if (inAction = Validate or inAction = ValidateAndPost); // calcs to validate the data if . . . some error condition . . . return RunFailed; endif; endif; if (inAction = Post or inAction = ValidateAndPost); // calcs to post the data if . . . some error condition . . . return RunFailed; endif; endif; return SuccessfulRun; end-proc;
Notice the variable, function, and function parameter that are defined with these data types.
dcl-s Stat like(RunStatus_t); dcl-pi ProcessBatch like(RunStatus_t); inAction like(Action_t) const;
The compiler would not prevent me from assigning literals or comparing to literals, but I never do. I always refer to the constants.
return SuccessfulRun; if Stat = RunFailed;
One last point I’d like to make is that I can truly use these template variables as if they were real data types, at least in most cases. For instance, I can use one of the constants in the INZ keyword.
dcl-s Stat like(RunStatus_t) inz(RunFailed);
This is not foolproof. The compiler won’t stop me from assigning an invalid value to a variable or assigning a literal for one type to a variable of another compatible type. Avoiding such errors is a matter of discipline, but it’s not an egregious task, and usually the errors stick out like a sore thumb. For example, this looks reasonable:
Color = Blue;
But this does not:
Color = RunFailed;
The newer languages certainly have their place, but that doesn’t mean that we should dump RPG overnight under the theory that anything new has to be better than anything old. RPG is an effective business programming language, and believe it or not, can do a lot that some critics say it can’t do.
Interesting article, Ted.
It would not be difficult to check within the subprocedure to ensure the input variable contains one of those numerated values and return, say -1, if not. Thus strengthening the association of the enumerated list with the template variable.
I’d love to see the Compiler offer a mechanism for enumerated values of a variable; or DB2 for that matter.
IBM has ‘toyed’ with the idea from way back; as with the VALUES keyword in DDS.
IBM has certainly brought RPG a long way to becoming truly competitive with other modern languages, that’s for sure! But there remains some challenges to be met.
Thank you for your advocacy with thoughts such as this one.
Hello Ted.
I use this DS:
//
D Task DS qualified
D FillSFL 2s 0 inz(96)
D Error 2s 0 inz(97)
D BackPgm 2s 0 inz(98)
D ExitPgm 2s 0 inz(99)
D Init 2s 0 inz(0)
D Continue 2s 0 inz(1)
D Find 2s 0 inz(10)
D GetData 2s 0 inz(20)
D ShowSFL 2s 0 inz(30)
D Print 2s 0 inz(40)
D Selected 2s 0 inz(50)
You can use RDI/WSDC autocomplete like: Task.Find
Best Regards,
Jose
There is an RFE requesting a “real” enumerated data type for RPG. http://www.ibm.com/developerworks/rfe/execute?use_case=viewRfe&CR_ID=145920.
One of the comments has some information from IBM about what it might look like for RPG. The “typed enum” form would probably involve restrictions on what could be assigned to a variable defined LIKE(theEnum) or passed to a parameter prototyped with LIKE(theEnum).