The real power of procedures is revealed in this article through the use of parameters and return values.
This is the last article in this series on Prototyping for Productivity. This is where I finally put all the pieces together and remove a third of the code in my little sample program. More impressive, though, is that, through the magic of D-specs, I turn 13 executable lines of code into four. While there just wasn't much code in this particular program to remove, in a real production application, the amount of code removed can be significant. Not only that, it's the type of repetitive code (validation, formatting, etc.) that is naturally prone to typographic errors—the kind that compile but cause errors.
Letting Parameters Do the Work
As I said, this article is the fourth in a series of articles. The first article showed you how to use a prototype to replace the PLIST in your programs, and the second article explained how to replace a subroutine with a subprocedure. The third article took a side trip to do a little investigation of the variable-length fields using the VARYING keyword. In this last installment, I'll show you how a couple of simple keywords can allow you to really take advantage of subprocedures to reduce your code.
Meet the Old Program
As I like to do in this series, let me reacquaint you to our program as we left it:
A h option(*srcstmt: *nodebugio) dftactgrp(*no) actgrp(*new)
B d ShowData pr extpgm('PRO1SND')
B d Data 512a
C d TagThis pr
C d Tag 10a varying
C d Value 80a varying
C d TaggedValue 128a varying
D d aTag s 10a varying
D d aValue s 80a varying
D d aTaggedValue s 128a varying
E d aBuffer s 512a varying
E d OutBuffer s 512a
/free
F aTag = 'FIELD1';
F aValue = 'Field 1 Value';
F TagThis( aTag: aValue: aTaggedValue);
F aBuffer = aTaggedValue;
G aTag = 'FIELD2';
G aValue = 'Field 2 Value';
G TagThis( aTag: aValue: aTaggedValue);
G aBuffer += aTaggedValue;
H OutBuffer = aBuffer;
H ShowData( OutBuffer);
H *inlr = *on;
/end-free
I p TagThis b
I d pi
I d Tag 10a varying
I d Value 80a varying
I d TaggedValue 128a varying
I /free
I TaggedValue = '<' + Tag + '>' + Value + '</' + Tag + '>';
I /end-free
I p e
A: My standard header specification for ILE programs.
B: A prototype for the PRO1SND program (this sends the value to the user in a message).
C: This is the internal prototype for the TagThis subprocedure. This formally declares the parameters that will be passed.
D and E: My temporary variables, most of which are variable-length fields. The first three are used to communicate with the TagThis subprocedure, and aBuffer is my work buffer. OutBuffer is my only fixed-length field and is used to send the data to PRO1SND.
F and G: These are the calls to TagThis. I pass the tag and value to TagThis, and it creates a tagged value. I concatenate those together into aBuffer.
H: ShowData calls the program PRO1SND, and since PRO1SND is a traditional program using fixed-length fields, I have to convert the variable-length field to a fixed-length field before I call. I assign aBuffer to OutBuffer and RPG automatically pads the field with blanks. I then pass the fixed-length field to the called program and exit the program.
I: TagThis creates the tagged value. Thanks to the wonders of variable-length fields, it requires only a single executable line of code to take the first two fields and create a tagged value of the format <TAG>VALUE</TAG>.
Simple program and relatively efficient. But now it's time to really squeeze it.
The Many Uses of CONST
The first of two modifications is the strategic addition of the CONST keyword. CONST is a keyword that modifies how a parameter works and has two very different but equally powerful capabilities. Like VARYING, these capabilities are useful in either ILE or non-ILE environments, because you can use prototypes to call external programs as well as procedures. I'll show you both situations in this example.
When you specify CONST on a parameter definition, the high-level effect is that the compiler makes the parameter read-only. You can think of the compiler as making a copy of your parameter before passing it to the called program or procedure. This has a number of side effects, two of which can be very advantageous. The first positive benefit is that the compiler will do conversions for you. It will, for example, convert a packed value into a signed value, or more importantly to this example, it will convert a variable-length field into a fixed-length field. The second benefit is sort of a corollary to the first: you can pass a literal to a program or procedure. That is, you can specify a literal value, either a named constant or even a literal, right in the call.
Together, these two capabilities remove a lot of code by removing the need for internal working variables. This is the new version:
A h option(*srcstmt: *nodebugio) dftactgrp(*no) actgrp(*new)
B d ShowData pr extpgm('PRO1SND')
B d Data 512a const
C d TagThis pr
C d Tag 10a varying const
C d Value 80a varying const
C d TaggedValue 128a varying
D d aTaggedValue s 128a varying
E d aBuffer s 512a varying
/free
F TagThis( 'FIELD1': 'Field 1 Value': aTaggedValue);
F aBuffer = aTaggedValue;
G TagThis( 'FIELD2': 'Field 2 Value': aTaggedValue);
G aBuffer += aTaggedValue;
H ShowData( aBuffer);
H *inlr = *on;
/end-free
I p TagThis b
I d pi
I d Tag 10a varying const
I d Value 80a varying const
I d TaggedValue 128a varying
I /free
I TaggedValue = '<' + Tag + '>' + Value + '</' + Tag + '>';
I /end-free
I p e
A: This doesn't change.
B and C: The prototypes for both of these use the CONST keyword on the parameters. In the case of TagThis, the third parameter is not marked as CONST because we need it to return the modified value to the caller.
D: Only one temporary variable. The changes in F and G remove the need for the others.
E: Only one interface variable. Because I've defined the parameter to ShowData as constant, I don't need a fixed-length interface variable.
F and G: Now we start actually removing lines of code (and variables!). I don't need to set aTag and aValue to literal values and then pass them to the formatting routine. Instead, I can specify those literals right on the call.
H: Another line of code disappears because I can pass the variable-length field directly on the call, and the compiler will convert it to a fixed-length field for me.
I: No changes here.
Can you see how this simplifies the code? And while the example is trivial, the technique has real power. For instance, I can pass an expression. For character values, I can use BIFs like %trim and %replace, and for numeric values, I can write calculations, all on the call. No work variables (and the associated problems of keeping them sized correctly).
The Last Step: Return Values
It's time to put the icing on the cake. I still have a work variable, aTaggedValue, that I have to pass back and forth between routines. It's not horrible, but it does require an extra field and it's one more place where I can forget to change a length. A better way to handle this is through the use of a return value.
A h option(*srcstmt: *nodebugio) dftactgrp(*no) actgrp(*new)
B d ShowData pr extpgm('PRO1SND')
B d Data 512a const
C d TagThis pr 128a varying
C d Tag 10a varying const
C d Value 80a varying const
E d aBuffer s 512a varying
/free
F aBuffer =
F TagThis( 'FIELD1': 'Field 1 Value') +
G TagThis( 'FIELD2': 'Field 2 Value');
H ShowData( aBuffer);
H *inlr = *on;
/end-free
I p TagThis b
I d pi 128a varying
I d Tag 10a varying const
I d Value 80a varying const
I /free
I return '<' + Tag + '>' + Value + '</' + Tag + '>';
I /end-free
I p e
This is the coup de grace! Let' review the changes.
A and B: No changes.
C: The TagThis prototype is different. The third parameter has been removed, and the prototype definition (the first line, with the "pr") has been changed to indicate that TagThis returns a 128-character variable-length field. So now, rather than updating a parameter, TagThis returns the result.
Section D is gone; we no longer need the variable aTaggedValue at all.
E: Doesn't change.
F and G: This is the real change to the programming. Because TagThis now returns a value, the calls to TagThis can be included on the right side of an assignment operation. Instead of having to write individual calls to set work variables and then using those work variables, I can set aBuffer to the result of the first call concatenated to the result of the second call, all in one line.
H: No change.
I: This is the other half of the programming change. The procedure interface (the line with the "pi") is modified in a way that mirrors the changes in C. The third parameter is removed; instead, the subprocedure will return a value. And rather than setting the contents of a parameter to the formatted result, the subprocedure instead uses the return opcode to return the value to the caller.
That's it. If you go all the way back to the original program, you'll find that we've removed most of the working variables and two-thirds of the executable code. That's the strength of procedures and one of the reasons that ILE RPG deserves a place among the modern programming languages of today's application development world.
This isn't perfect. Return values can be used only on subprocedures (either internal or external), not on calls to programs. Also, prior to 7.1, a field returned from a subprocedure is treated as a "by copy" field, meaning that the subprocedure uses an internal copy of the variable, which is then copied to the caller's variable when the procedure returns. This is a potential performance issue on large return values, but the RPG compiler team fixed that with a keyword (RTNPARM) that turns it into a pass by reference. I also still have one working variable; I'll leave it as an exercise for you to identify how to remove that last field.
Parameters Are Your Friends
I hope this article has shown you the benefits of using parameters and return values and the ways some of their keywords can reduce your programming load. I also hope the whole series has given you a reason to start using prototypes (especially for subprocedures). I know this is a trivial program; it was designed specifically just to show you the features. In another series of articles, I'd like to present some examples of using these capabilities to address real-world programming issues. Until then, good luck with prototypes!
LATEST COMMENTS
MC Press Online