Guru: Fall Brings New RPG Features
December 7, 2020 Jon Paris
In this time of pandemic we could all do with a little cheering up. So “Santa” Barbara (a.k.a. Barbara Morris) and the elves at the IBM Toronto Lab have delivered an early Christmas present. Available now via PTF for 7.3 and 7.4, there are some real gems in these latest RPG enhancements.
For the most part, these enhancements assist in improving code readability. That is to say that they are not giving us completely new functionality in the way that (say) Open Access or DATA-INTO did. Rather they give us better, clearer ways of doing things. They have an additional benefit in that by implementing functionality that is common in other languages, they help make the transition to RPG easier for newcomers to the language.
Let’s start with two of these enhancements, best demonstrated with an example. Suppose that you have an item-type code that must be “A”, “B”, “C” or “Z”. A conventional IF statement to check if the code is valid might look like this:
IF ( typeCode = 'A' ) or ( typeCode = 'B' ) or ( typeCode = 'C' ) or ( typeCode = 'Z' );
Of course, the more options there are the worse the code gets. Compare that with using the new BIF %LIST together with the new IN operator. As you can see, it simplifies the coding significantly:
IF typeCode IN %List( 'A' : 'B' : 'C' : 'Z' );
Now isn’t that a lot cleaner and more obvious? Big, big improvement in my opinion. And even better if you want to test for a code NOT being in the list. A word of caution though. My first thought was to simply add the keyword NOT immediately after the IF, as in the first example below, but this does not work. Rather, you need to place the whole condition in parentheses and then negate the result with NOT as in the second example.
// This version will not compile IF NOT typeCode IN %List( 'A' : 'B' : 'C' : 'Z' ); // But this one does IF NOT ( typeCode IN %List( 'A' : 'B' : 'C' : 'Z' ) );
It makes sense if you think about it but I thought I’d mention it just in case your initial reaction was the same as mine.
There are other uses for both the IN operator and %LIST beyond this type of test, although frankly, I’d be happy enough if this was all they did. Let’s look at %LIST first.
%LIST actually represents a temporary array and can therefore be used as such. So if, for example, I had a series of non-contiguous fields that I wanted to be able to load into an array, I could use %LIST to simplify the task, like this:
fieldArray = %LIST( Field1 : Field2 : Field3 : Field4 : Field5 );
This will result in the contents of Field1 being assigned to element 1, Field2 to the second element, and so on. I think you will agree that this is a lot cleaner and simpler than a series of assignments to the individual array elements.
In fact the %LIST option is so flexible that it allows not just simple fields and constants, but expressions and procedure calls as well, as long as the base data type matches the array definition. For example, if the data type returned by GetNearestWarehouse() matches that of the warehouseList array, this code would work just fine.
warehouseList = %List( preferredWarehouse : GetNearestWarehouse(accountNumber) : 'Chicago' );
You might wonder, since %LIST represents an array, whether IN can also be used to search a conventional array. The answer of course is yes. Here’s a comparison between doing it the “old” way and using the IN option:
If %Lookup( 'Two' : fieldArray ) > 0; Dsply 'I found it!'; EndIf; If 'Two' IN fieldArray; Dsply 'I found it!'; EndIf;
In this case I searched for the literal “Two” but the search argument can be anything that is compatible with the data type of the array being searched. Once again, a little simpler and more obvious I think.
Not Only %LISTs But Also %RANGEs
The new IN option is not just limited to working with %LISTs. It can also be used to test if a value is in a specified range by using the %RANGE BIF. Here’s an example of it in use:
If orderValue IN %Range( minimumOrder : customerMaximum ); // Is equivalent to If ( orderValue >= minimumOrder ) and ( orderValue <= customerMaximum );
The range values can be literals, fields, named constants, etc. etc. Again they simply have to match the base type of the comparison value. Once again, it’s not something we couldn’t do before, but a big improvement nonetheless. Just in case you are wondering, %RANGE is inclusive. That is, it includes the from and to values. So for %Range( 3 : 6 ), the values 3, 4, 5, and 6 are all considered in the range.
What Else Did Santa Bring?
There are two other significant enhancements in this package. The first is the arrival of FOR-EACH loops in RPG, something I have been hoping for since encountering them in PHP. The second provides new compiler options to resolve issues related to character-to-numeric conversions. I will be covering both of these features in my next Tip.
To see the full list of enhancements and the PTFs that are needed to implement them go the RPG Cafe page at https://ibm.biz/rpg_cafe.
I hope you find these new features as useful in your coding as I know I will.
Jon Paris is one of the world’s foremost experts on programming on the IBM i platform. A frequent author, forum contributor, and speaker at User Groups and technical conferences around the world, he is also an IBM Champion and a partner at Partner400 and System i Developer. He hosts the RPG & DB2 Summit twice per year with partners Susan Gantner and Paul Tuohy.
Brilliant Jon! Santa has absolutely delivered.
Can’t wait to get my hands on these new bifs.
Thanks for the write up and especially for the “not in list” example – I would’ve done exactly what you did.
This seems to make more sense to me coming from an SQL background: IF typeCode NOT IN %List( ‘A’ : ‘B’ : ‘C’ : ‘Z’ );