The questioner in this case needed to truncate the result field on an EVAL operation in the same manner that a Z-ADD would do. As is usual in such cases, responders posted many different solutions to the problem--several of the solutions were truly ugly. Certainly the majority would have required substantive commenting in order to avoid confusing the heck out any future maintenance programmers.
When /Free came out we were very much in favor of dropping some of the more arcane RPG op-code such as MOVE and ADD. ADD because EVAL was a much better option and we always thought old-style RPG's numeric truncation was an extremely dangerous "feature" that we were better off without. MOVE because you had no idea what it was doing until you knew the data types and lengths of both fields. We won't get back into the MOVE discussion here, but this latest go-round of postings did make us wonder about solutions for the truncation issue.
One of the few valid uses for numeric truncation that we have encountered involves the rolling-over of counters. For example, to avoid generating duplicate keys, some people use a rolling counter coupled with the time. If you use a four-digit counter, it is useful to have it roll directly from 9999 to 0 when one is added to it. Note that we said "useful" and not "good." It was always poor technique because what it is doing (the roll-over) is not obvious when looking at the code. Regardless, to achieve the same thing in /Free requires that you test the value of the counter before incrementing it and then increment or zero it as appropriate. At least what is happening is more obvious but it does clutter up the mainline logic.
Normally in such circumstances we would recommend that folks use a simple generic subprocedure to implement the function. For the sake of argument let's call it IncrementCounter. This has the advantage that everyone knows its purpose when seeing it in the code and will know that it rolls over on hitting the maximum. It also has the advantage of hiding the complexity of the logic and, as we have said many times before, using subprocedures in this way saves a lot of time in understanding what is going on in the code just so you can ignore it.
But the whole discussion did make us wonder whether there is indeed a place for a new op-code (EVALT) or op-code extender EVAL(T) in RPG. It would handle the scenario posed in the original question on the RPG list and it would also handle the roll-over scenario. We tend to lean toward the use of a T(runcate) extender on numeric EVALs because there is really no need for a new op-code and also because extenders are not frequently used so they tend to jump out at you and make their use obvious. Indeed since EVAL itself is rarely used in /Free an EVAL(T), it really is going to stand out.
What do you think? Is this the kind of simple RPG extension that is worth developing? Let us know via the blog comments.
P.S. No, we haven't changed our minds about implementing MOVE in /Free. That was one death we were pleased to see.





eval(T) has my vote. Apart from the mentioned counter wrap, I would occasionally use it for one-shot utility pgms where a DSPF field is smaller than its database counter-part - and yes, I know these have a nasty habit of becoming production pgms. Would also prefer eval(T) in converted RPG III over the various work arounds for numeric fields.
Posted by: Robert Clark | January 17, 2013 at 03:57 AM
Jon,
I was one who suggested (in the midrange.com discussion you wrote about) that IBM should have created a EVAL(T) op-code extender. Although I do love /FREE I wonder if, had IBM designed a fuller equivalence between /free and non /free code, its implementation and acceptance would have been quicker.
The first time I was able to program in /free (on a V5R3 machine, as we upgraded straight from an old V4R5 720), the first thing I did was to convert every program I had to modify, not matter how small the change, to free format (with WDSc - not RDP in those days). I quickly found that some things stayed in fixed format (eg. MOVE), giving way to a strange code mix.
My favorite “peeve” is SCAN. In order to being able to use it I have to either emulate it, using free code, or encapsulate it between /end-free and /free (which I absolutely refuse to do).
So yes, I believe that IBM should erase any differences between /free and “fixed” code, not giving anyone excuses for staying in the dark ages (even if that means implementing MOVE in /free :-) )
Best Regards,
Luis Rodriguez
Posted by: Luis Rodriguez | January 17, 2013 at 07:25 AM
I feel that an EVAL(T) opcode would be befeficial.
Presently, when checking for a "rollover" condition - rather than saying something such as "if variable = 999" or "if variable = 9999" etc, I use "if variable = *all'9'" - that way, variable length doesn't matter.
Posted by: Dan Devoe | January 17, 2013 at 09:13 AM
Is not the %REM BIF enough to solve this problem without further complications? In this case I do not see the need for an EVAL(T)op code.
IMHO, RPG provides enough resources to replace old op codes and document (which I consider the most important part) the intention of the programmer.
Posted by: Gerardo Agüero | January 17, 2013 at 10:48 AM
@Luis
"I wonder if, had IBM designed a fuller equivalence between /free and non /free code, its implementation and acceptance would have been quicker."
No - I don't think it would have made any difference. The vast majority of people I have met who refuse to use /Free use things like MOVE as an excuse. The reality is that they don't want to change. There are a few cases where I would still like to see a direct replacement - for example something like CAST (or CONVERT I guess) to replace the type changes possible with MOVE. But for the most part I think it was way past time to drop the more error prone functions of the language like MOVE and move (pun intended) ahead.
P.S. I still remember when an attendee at COMMON, who had insisted for many years that MOVE was not difficult to understand or error prone, came up to me and apologized. He had just spent his first 24 hour period dealing with a bug introduced by a programmer who didn't understand that MOVE's flexibility could also be a trap for the unwary - and even the wary for that matter.
"My favorite “peeve” is SCAN. In order to being able to use it I have to either emulate it, using free code, or encapsulate it ..."
I don't quite understand this as %Scan replaces it. The only real difference is that %Scan will only report a single instance and does not have the array option of the old SCAN op-code. Personally I'd like to see that as an option but given the ability of %Scan to be embedded in an expression it would be ugly stuff to implement.
If I need the old array output option I just code it as a subproc sp the looping is "hidden"
Posted by: Jon Paris | January 31, 2013 at 06:02 PM
Jon,
I do agree that, as a replacement for the differences between the SCAN op-code and the %SCAN() BIFF the solution is, as you wrote, write a subproc (which is, in fact, the route I chose, as I don’t really like to use the /free - /end-free pair). As per MOVEs, I tend to use data structures, so that one is also a non-issue for me.
My only question is: why? Why did not the language include this when the BIFFs were created?
Regarding your CONVERT suggestion, I suppose we can do with %DEC(), %CHAR() and/or %EDITC(). Just the same, I don’t like to give excuses to anyone for not going the /free way (and yes I know that, most of the time they are just that, excuses :-) )
Regards,
Luis
Posted by: Luis Rodriguez | February 04, 2013 at 10:07 AM
Luis, the reason that %SCAN doesn't have a way of returning an array like the SCAN opcode is that RPG supports using built-in functions in array calculations like this:
positions = %scan('ABC' : strings);
This means the same as
for i = 1 to minimum elements;
positions(i) = %scan('ABC':strings(i));
endfor;
Any of the operands on the right hand side of an assignment can be arrays as long as the left hand side is an array.
Even though most RPG programmers don't take advantage of this, we thought it would be confusing (to the compiler if not to RPG programmers) if a built-in function could sometimes return an array, which would mean that %SCAN would not behave the same as the other built-ins in "multiple element indexing".
The same reason precluded supporting arrays for %SCANRPL, even though it would have been nice to code
newString = %SCANRPL(fromArray : toArray : oldString);
Speaking of %SCANRPL, I'm wondering if %SCANRPL has taken away most of the reasons for wanting %SCAN to return all the instances.
Posted by: Barbara Morris | February 13, 2013 at 10:02 PM
I'd say there is no need for EVAL(T). When converting old fixed-format code into free I would recode pieces which cannot be easily converted like MOVE operations and - yuk - GOTO, CABEQ etc. So the source is really being modernized and gets more structured. Converting legacy code into free-format just to give it a more modern look doesn't make too much sense to me.
> positions = %scan('ABC' : strings);
Thanks Barbara, didn't know this. Great!
Best,
Markus
Posted by: Markus | February 14, 2013 at 08:27 AM