Skip to main content

RPG's New Procedure Overloading and DATA-GEN Capabilities

RPG experts Jon Paris and Susan Gantner explain new capabilities available with the Fall 2019 IBM i TR.

Every year at about this time, IBM usually delivers a Technology Refresh (TR). Usually with that TR comes new RPG features. This fall is no exception and RPG has delivered three new features, two of which are "biggies."

The first is procedure overloading, something many of us have been anticipating for a long time. 

The second is a brand new "companion" op-code to keep XML-INTO and DATA-INTO company. It’s called DATA-GEN and provides RPG with the ability to turn a regular RPG data into a JSON document, or HTML, or XML, or ... basically any type of formatted data. In other words, it does the opposite of what the XML-INTO and DATA-INTO commands do.

The last of the three is much smaller but nonetheless useful. It’s an additional option to ensure that prototyped parameters are an exact match to the specification.

Procedure Overloading

If you’re familiar with any Object Oriented programming languages, then the concept of procedure overloading should already be familiar to you. For those of you who don’t have that experience, the best way to explain it is by way of an example. 

Suppose we currently have a simple subprocedure that accepts a date as its input and returns a number representing the day of the week in the range 1 through 7. Now suppose that we get asked to create a new subprocedure that performs the same task but accepts one of our old 7-digit packed decimal "dates" as its input. One way of dealing with this request would be to tell the programmer in question to "go away" and use the %DATE built-in function (BIF) as the parameter to the existing routine. Another, more polite, approach would be to create a new subprocedure with a different name and parameter list and tell the programmer to use that routine instead. Under the covers, of course, the new routine would probably simply format a date and then call the original routine. 

This is all well and good until someone else points out that the company also has some 8-character fields that contain "dates" and it would be really nice if your routine could handle those as well. But doing so would make for three routines, all performing the same basic function but with different names. That’s confusing at best.

Enter procedure overloading: With this capability we can now use a single function name and have the compiler work out which routine to actually call! As you can see, such a facility goes well beyond what we can do with existing parameter options such as CONST and VALUE.

Let's look at a quick example:

(A) Dcl-Pr DOW_Date  int(5);

       inpDate  date(*ISO)  Const;

    End-Pr;

 

(B) Dcl-Pr DOW_Num  int(5);

       inpDate  packed(7)  Const;

    End-Pr;

 

(C) Dcl-Pr DOW_Char  int(5);

       inpDate  char(8)  Const;

    End-Pr;

     

(D) Dcl-Pr  DayOfWeek  int(5)  

            OVERLOAD( DOW_Date : DOW_Num : DOW_char);

 

(E) dayNumber = dayOfWeek(aDate);

    dayNumber = dayOfWeek(numDate);

    dayNumber = dayOfWeek(charDate);

At (A), (B) and (C) you can see the prototypes for the three procedures. Yes, we still need one for each of the different types of parameter but the difference is on the invocation of the procedure. No matter which of the three input types we are dealing with we can simply call DayOfWeek. You can see examples of the call at (E).

The mechanics are quite simple. The DayOfWeek prototype (D) just lists the names of the procedures it overloads as parameters to the OVERLOAD keyword. Also, note that the return value it specifies, if any, must be the same as the procedures listed. Prototypes that specify OVERLOAD cannot have an END-PR, and no parameters or any other keywords can be specified other than those related to the return value.

When the compiler encounters a call to DayOfWeek, it studies the actual parameter(s) passed to the function and decides which of the candidate procedures to actually use. So in our example, if the parameter were numeric then DOW_Num would be called. If a date field was passed then DOW_Date would do the work. You've probably guessed by now that DOW_Char will be called to handle character fields.

In our simple example, it is the data type and size of a single parameter that decides which routine to call. But it could also have been based on the number of parameters as well as their data types and sizes.

What happens if no procedure is listed that matches the parameter(s) being passed? The answer is that the compiler would simply issue an error message. But what if there were two procedures that matched the parameters passed? Right now, the compiler will simply error out this situation. We hope that at some time in the future this may change such that the compiler would apply a "best fit" approach. But for now, IBM has erred on the side of caution and until we get used to this new capability this is probably a good thing.

To avoid code duplication we have coded both DOW_Char and DOW_Num to eventually call DOW_Date to do the actual work. To demonstrate what we mean we have shown the code for DOW_Num below. The code for DOW_Char would be very similar.

In our example, we’re assuming that all of our numeric "dates" are in *CMDY format. If this were not the case, we would need an additional parameter to specify the format that it represents and use additional logic to make the conversion.

At (F) you can see that the input parameter is tested to see if it actually represents a valid date in *CMDY format. If it doesn’t, then the day number is set to -1. However, if it represents a valid date, then it is converted to a date (H), which is then passed to DOW_Date to calculate the day number (I). Finally, the day number is returned to the caller (J). Yes, we could have done the whole thing in less steps by, for example combining the %Date and DOW_Date calls but we wanted to make it clear what was going on.

Dcl-Proc DOW_Num;                        

                                             

    Dcl-Pi DOW_Num  int(5);                  

       inpDate  packed(7)  Const;            

    End-Pi;                                  

                                             

    Dcl-S workDate   date(*ISO);              

    Dcl-S dayNumber  int(5);

                                         

(F) Test(DE) *CMDY inpDate;                  

                                             

    If %Error;                               

(G)    dayNumber = -1;                            

    Else;                                    

(H)    workDate = %Date( inpDate : *CDMY );   

(I)    dayNumber = DOW_Date( workDate );

    EndIf;

   

(J) return dayNumber;

 

    end-proc;

New Prototyping Option

The addition of parameter prototyping was a great advance in the RPG language; it provided parameter validation on program and procedure calls. But it has always had a shortcoming in one particular area. While it prevents us from shooting ourselves in the foot by passing (say) a six-character field when the called routine expects eight characters, it doesn’t prevent you from passing a field that is longer than required.

Most of the time this doesn't matter but once in a while it does. For example, if the called routine places a value in the field it will only fill (in this case) eight characters. Whatever was in the last two characters will remain untouched, and that can be a problem.

Enter OPTIONS(*EXACT)

When you use this option the rules on what is an acceptable parameter are strengthened. Try to pass a 10 character field when the prototype calls for 8 characters and the compiler will reject it.

Even better, this support is extended to data structures (DS). If a DS is passed as a parameter by specifying LIKEDS and OPTIONS(*EXACT) is specified then the DS being passed must be directly related by LIKEDS to the same DS.

Look at the code below and you'll see what we mean:

dcl-s char8    char(8);              
dcl-s char6    char(6);              
                                       
dcl-ds Ds1 Qualified;                 
  A char(10);                        
  B char(20);                        
End-Ds;                                
                                       
dcl-ds Ds2 LikeDS(Ds1);               
                                       
dcl-ds Ds3 Qualified;                  
  A char(10);                        
  B char(20);                        
End-Ds;                                
                                       
Dcl-Pr Target1;                           
  parm1 char(6) Options(*Exact);     
End-Pr;                                
                                       
Dcl-Pr Target2;                           
  parm1 LikeDS(Ds1) Options(*Exact); 
End-Pr;                                
                                       
Target1(char6); // This is good 
        
Target1(char8); // This will fail - parm is too long
                                       
Target2(Ds1);   // This works 
                  
Target2(Ds2);   // So does this
                 
Target2(Ds3);   // But this fails even thought the DS is identical           

We like this feature. It always worried us that longer parameters were accepted and this remedies that situation. Hopefully this will foreshadow some other enhancements in this area. For example, we'd like to see an extension to the CONST parameter option to specify that a copy of the data should always be made thereby providing for true read-only parameters.

Currently, CONST only copies the data when it has to match the data type and/or size of the prototype parameter. We’re not sure what name to give such an option—*COPY perhaps. We'd be interested to hear what others think.

DATA-GEN

One of the most requested features for RPG in recent years was for IBM to provide an operation that would reverse the functionality of XML-INTO. XML-FROM was often suggested as the opcode name, the idea being that whereas XML-INTO decomposes an XML document and unloads it into a DS, "XML-FROM" would do the opposite—taking the data in a DS and formatting it as an XML document. There are a number of potential problems in providing such a facility, not the least being that XML is just one of many potential output formats. What about JSON? Or HTML, or CSVs?

That said, IBM has chosen not to provide "XML-FROM", rather giving us something far more valuable: DATA-GEN. DATA-GEN follows the same path that IBM took with DATA-INTO, i.e., they’ve provided an opcode framework into which users can plug their own generator. This opens the door to generating any document type you like, including company specific data interchange formats, or for interchange standards that have not even been invented yet!

Among the generator samples that IBM is supplying are a simple generator that produces HTML, and one that generates XML. As usual, these are intended as primarily as demonstrations and are not production level code. In time more complex generators will be forthcoming from third parties. Hopefully Scott Klement will add a JSON generator to his YAJL toolkit, just as he added a DATA-INTO parser for JSON.

Because of the complexity involved in writing a generator, it will take us a few weeks to be in a position to write up this new functionality in the detail it deserves and requires. We'll be back with another article shortly.

Moving RPG Forward

We’re excited by these new options, but even more excited by the fact that IBM continues to move the RPG language forward. Don't let anyone try to convince you that RPG is a dying language—each new release proves otherwise.  

Webinars

Stay on top of all things tech!
View upcoming & on-demand webinars →