Monday, May 23, 2016

ILE RPG - Sub-procedures vs. Subroutines

When writing new RPG code, I use free format and sub-procedures exclusively. There is no advantage to using the older fixed format or subroutines. Fixed format limits the space you have for expressions, and prevents nicely indented control structures while there is nothing that a subroutine can do that a sub-procedure cannot. Even when maintaining existing code I tend to use free format and sub-procedures unless it is significantly more convenient to do otherwise. that being said, there are a few things to take into consideration. Particularly when it comes to using sub-procedures vs. subroutines.

Sub-procedures control scope as well as providing for code reuse. Subroutines are all about code reuse. It is the scope of a sub-procedure that makes is so useful. Items defined within a sub-procedure cannot be used or referenced outside it, and override similar items defined in the global scope. This is fairly well understood for variables defined with dcl-?.  For example:

dcl-s ix      Int(5) Inz(10);

procA();
dsply ix;
return;

dcl-proc procA;
  dcl-s ix    Int(5) Inz(1);

  dsply ix;
end-proc;

Will result in:
1
10

Displayed on the console. Why? Because ix in procA is different from the global ix declared above. Sub-procedures allow me to pass in parameters, to allow even easier code reuse. No need to set some global variables, then call the subroutine, then retrieve the result out of other global variables. I simply pass the necessary values as parameters, and retrieve the result as a return value, or from one or more output parameters.

field1 = 'A';
field2 = 15;
exsr convert;
result = field3;

vs.

result = convert('A': 15);

It is this capability of sub-procedures that compels me at times to convert existing subroutines to sub-procedures. Particularly if a subroutine is working with a single set of values, and I need to call it with a different set of fields. This can be a simple conversion, just change BEGSR to DCL-PROC, ENDSR to END-PROC, add parameters, and pass them properly in the call. This is where a hidden feature of the subroutine becomes apparent. A subroutine is not a global entity. It is always scoped to a procedure, either the main procedure, or a sub-procedure. If you simply convert a subroutine to a sub-procedure, but the subroutine contained an EXSR op code, that called subroutine will suddenly be out of scope for the new sub-procedure. To fix this you will need to convert these subroutines called by the sub-procedure you just converted, and any subroutines called by them to sub-procedures as well. This additional work may take the use of a sub-procedure in this particular instance and put it in the realm where just setting and retrieving global variable values is significantly more convenient than converting all the necessary subroutines (and their calls) to sub-procedures.

Here is a quick diagram of how procedures, sub-procedures and subroutine scopes work within an RPG program.

Main Procedure {
   subroutine A
   subroutine A1
}

sub-procedure 1 {
   subroutine B1
   subroutine B2
}

sub-procedure 2 {
   subroutine C1
   subroutine C2
}

Anything in the Main procedure can call sub-procedure 1 or 2, and subroutines A1 or A2, but not subroutines B1, B2, C1, or C2. Sub-procedure 1 can call sub-procedure 2, and subroutines B1 and B2, but not any of the others, and likewise for sub-procedure 2. this is kind of sudo-code as curly brackets are not a part of the RPG syntax. In fact, a main procedure has no explicit starting point or ending point unless it is a liner main procedure. This is just an easy way to visualize the scope.

What do you think, was this helpful to you?