20
Thu, Jun
3 New Articles

Advanced/Free - Function Calls and Return Values

RPG
Typography
  • Smaller Small Medium Big Bigger
  • Default Helvetica Segoe Georgia Times

Even though we have used /Free to build some ILE programs, there are still a few more tricks for you to learn about the latest iteration of RPG.

Editor's Note: This article is excerpted from article 11 of 21st Century RPG: /Free, ILE, and MVC, by David Shirey.

We are going to start by talking about two related topics: function calls and return variables.

The first thing I want to say is that this is a technique that can only be used to call a sub-procedure that is embedded in a service program or that is embedded in the module you are doing the function call from. Remember before how we did that with a CALLP? Well this is another way to do that without the CALLP. But the function call cannot be used to call one program from another; that must still be done with a CALLP. It only works when calling a sub-procedure.

The second thing I want to say is these two things (function calls and return values) go together. You can’t do one without the other.

What Are “These Things”?

The function call allows you to dispense with the CALLP opcode and just express the “call or prototype” as an equality between a variable and a function, similar to the way you would in PHP or another Web language. It’s kind of freaky, and I have a certain tendency toward vertigo when I use it, but it is very popular in Web languages, will get more common in RPG as we move into the future, and is way cheaper than Ecstasy.

Return values deal with getting something back from the called program, something that is not one of the parameters that is passed with the prototype. That is, we have already seen how you can send parameters along with the call and then get them back when the CALLP is over. But what if you want to have other variables returned from the sub-procedure or program beyond what is in the parameters? That is where return values come in. And a function call (as opposed to CALLP) is the only way you can work return values into the mix.

But remember, this type of call only works when you are calling a sub-procedure (either in a service program or one embedded in the program you issue the call from). You cannot use it to call an independent program.

Compile Notes

I really shouldn’t bother you with this because it’s pretty obvious if you think about it, but—well, sometimes we don’t think unless we are told to, so let me just mention this.

If you are doing the function call/return values thing from a program to a service program, then remember that both programs will have to be compiled first with CRTRPGMOD (instead of CRTBNDRPG). Because of this, you won’t need the H-spec for DFTACTGRP in either program (because CRTRPGMOD assumes you are going to be ILE). But you can include an H-spec giving the ACTGRP that you want to use (more on what those activation groups are later). Then you will need to do the CRTSRVPGM for the called program (the service program) and the CRTPGM for the calling program.

If you are doing the function call/return values thing from a program that actually contains the embedded sub-procedure, then you can use the DFTACTGRP H-spec and just compile the program as CRTBNDRPG.

Function Call/Return Values: The Details

Let’s start with the function call. What is that again?

It is something that is actually very Web language-like: the ability to call a procedure through an evaluate statement. Hold it. Wait a minute! Where are you going?? Come back. It’s not hard to do. Honest! See, it doesn’t look scary. A little weird maybe.

DESCP = VAL_PRDNO(PRDNO);

That is, we call the sub-procedure, VAL_PRDNO, by equating it to a variable DESCP, which is defined in the sub-procedure. It has to be defined in the calling program as well; my point here is that it is coming from the sub-procedure and so needs to be valid in there as well.

This sub-procedure could be held in a service program whose name I could give a rip about because when dealing with service programs you never call the service program, just the sub-procedure (VAL_PRDNO).

Or the sub-procedure could be embedded in the program that we are in. What is important is that we are doing the call by doing an evaluate to a variable, DESCP.

Why DESCP? Because that is the variable (a description field) whose value we want to return from the sub-procedure. That is, DESCP is the “return value” of the “function call.” We pick what it is. As long as it is described in the program we are calling.

Please notice that DESCP is not a parm in the prototype that is defined for the VAL_PRDNO sub-procedure. But by using the function call and a few modifications to our calling and called program, we let the compiler know that we will be returning whatever variable is on the left side of an equality involving the sub- procedure. And yes, you are right. This is as close to black magic as you can get without actually having to drink blood.

As you can see, return values are inextricably linked to the function call. What happens if you do the function call but you don’t set things up to return a value? It doesn’t work. The compile fails because function calls and return variables go together like Beckett and Captain Hammer.

Similarly, if you want to do a return value, then you have to use the function call. That is, if you want to return a value outside of the parms listed in the prototype that is still required for the function call, then the only way to do it is to set up a function call and put the value variable you want returned equal to the sub- procedure name and parms.

So, given that, let’s look at both the calling and the called program to see what changes are required to make the return values and function call work. First, the calling program.

Calling Program

Let’s start this process by taking a look at the program where the function call is issued and see what changes have to be made to accommodate it and the return value.

Advanced/Free - Function Calls and Return Values - Figure 1

And now, once again, this time dissecting the program. Remember, this is the program that calls the sub-procedure held in the service program.

Advanced/Free - Function Calls and Return Values - Figure 2

First, we don’t need an H-spec to make this program ILE (because we will need to compile it using CRTRPGMOD since it is calling a service program, although we could use one to set the activation group) so we go right to the F-spec for the display file where I have defined the PRDNO value. And again, I have not included the logic to actually pick up that value in an effort to keep this simple.

Advanced/Free - Function Calls and Return Values - Figure 3

Then come the D-specs, which includes the PR. You might be tempted to think that with return values and a function call you wouldn’t need the PR, but that is not true. You always prototype to access another module in ILE. In this case, there is one subfield under that PR because that is how the sub-procedure is written. The D01_PRDNO value will be returned automatically if it is changed in the sub-procedure. Note that DESCP is not referenced at all in the prototype; return values variables are not part of the PR D-spec.

What is different is that the VAL_PRDNO PR D-spec has a length of 30 on it. Had you noticed that? So far we have not put a length on the PR line and with good reason. According to the Treaty of Versailles, the international symbol for “I got a return value coming back on this puppy” is a length on the PR line.

This is not the overall length of the parms in the PR (that is only 15) but rather the size of the field we want to get back in the return value (DESCP). You need to put that length here so that a pipeline is established to get that value returned.

Without it, the return values and the function call will fail in the compile.

Advanced/Free - Function Calls and Return Values - Figure 4

Then the logic statements. Notice that instead of a CALLP to VAL_PRDNO(D01_PRDNO), we are using an evaluate statement. I have put the CALLP in there and commented it out just so you can see it.

When the sub-procedure is accessed, the value for the DESCP field will then be returned to the DESCP field in this program.

Soooooo, to summarize, in the calling program we need to:

  • Put the length of the return values field on the PR, and
  • Use the function call, setting it equal to the field we want passed back.
  • Then, that field will not appear directly in the prototype

Called Program

And now, on to the called program. I have decided to make the call to a service program. Remember, it could also have been done to a sub-procedure that is embedded in the calling program. It cannot be done to an independent program (that call has to be done via a CALLP).

Advanced/Free - Function Calls and Return Values - Figure 5

Seriously, I am sorry if I am belaboring some of this, but I want you to get very familiar with looking at sub-procedures and service programs. The more you do that, the less likely you will be to fear and detest them, and the more likely you will be to consider them friends and compatriots in your fight against uh, whatever you are fighting against. Yourself most likely, I would guess.

Advanced/Free - Function Calls and Return Values - Figure 6

We start with the standard NOMAIN that is required for every service program.

Advanced/Free - Function Calls and Return Values - Figure 7

Followed by the F-specs for the file we will use to do the validation. Remember, if you are on 6.1 or above, this F-spec could go in the actual sub-procedure.

Advanced/Free - Function Calls and Return Values - Figure 8

Then the global D-spec for the prototype (the PR). If we had put this in with a copybook, then we would be using the COPY statement that is commented out. Note that we set a length for the PR line (30), the length of the field we are going to be passing back via the return value. This is required. We will see the same thing on the PI below because they must match.

What you might have not noticed is while PRDNO is the same on both the PR and PI here, we actually named the subfield D01_PRDNO in the calling program. Is this OK? Yes, it is. The names must match within a module, but they can be different between the calling and called programs. Not sure why you would want to do this, but it could come up. Anyway, carry on.

Advanced/Free - Function Calls and Return Values - Figure 9

Then the start of the sub-procedure. We include the EXPORT keyword because we are calling this sub-procedure outside of this service program. We will talk more about this keyword and exporting later.

Advanced/Free - Function Calls and Return Values - Figure 10

Followed by the D-specs for the sub-procedure including the prototype interface (PI). Again, please notice the length on the PI line indicating that a return value is to be set. Remember, the PR D-spec that we had outside of the P-spec could just as well have been set up inside here. And, if this service program had other sub-procedures that had different prototype subfields beyond just PRDNO, we would have had to set it up in here because a single PR would not work.

Advanced/Free - Function Calls and Return Values - Figure 11

Finally, we get to the logic. This example will be different from the ones we have used so far because the RETURN opcode is used only for specific situations. In this case, we are going to pick up a field from the MSPMP100 record that we read— here it’s the description (DESCP)—and return that. If no record exists, we return a -1, which is sort of standard for function calls that fail. Note that PM_DESCP is not specified in a PR or PI, but it is defined in the F-spec for that file.

If you indicate you are going to do a return value (by putting a spec on the PI line), then you must actually execute the RETURN opcode (so that is why we have two branches on the IF above), or else an exception will be issued.

Advanced/Free - Function Calls and Return Values - Figure 12

And then finally, the end to the sub-procedure and, in this case, the service program.

Advanced/Free - Function Calls and Return Values - Now It's Your Turn

And One More Thing

There is one more thing that I should mention, and that is that you can use the function call without having any return values.

In other words, so far we have used the function call in concert with a return value, so that the code looked like:

DESCP = VAL_PRDNO(D01_PRDNO);

But if there is no return value that you want to have sent back, if you’re just concerned with the call and its associated parms, you can dispense with the return value, and the result looks like this:

VAL_PRDNO(D01_PRDNO);

This format is wildly popular with a lot of people. It does look Web-like, and I have nothing really against it except, as I have said before, I used the CALLP as a visual clue that I was calling a module at this point in the program. But it’s pretty obvious that you are doing that. I guess. Anyway, expect to see this format a lot in the future.

Does It Matter?

Does it matter whether you use a CALLP or the evaluate method to access the sub- procedure? Well, like most things, the answer is not clear-cut.

I have seen a number of articles praising this option. Some people seem to use it almost exclusively. And there might be times when you want to take advantage of it.

For example, the word on the street is that using the function prototype is more efficient than using CALLP. I am sure it is true. And it is also probably true that in most cases, that is not going to make a whole lot of difference in your response time. But if you are doing a call many, many times in your program, it might make really good sense to use the function call instead of the CALLP.

Plus, the function call-return value approach is a very Web-looking thing. That is the way it is done in PHP, and you know what I think about that. (Actually, I wish PHP had a call function like CALLP, but let’s not go there.) Of course, if PHP is going to jump off a bridge, does that mean you are going to do it, too?

But it is possible that being able to say that we can do this kind of thing in RPG is one step to making our language look a little less stuffy to Web types. And perception is important.

But on the whole, for me, there are a number of things that make me say that for general use, it wouldn’t be my choice.

First, it doesn’t allow you to eliminate the PR and PI stuff. If it did, then that would be a real plus (maybe), but it doesn’t.

Second, it doesn’t allow you to return more than one parm. Granted, you could return a data structure and then break that down, but that is extra steps and extra code on top of the already-existing PR.

Nor do you want to think of this as an emergency way to get anything you want out of the sub-procedure without changing the parms.

That is, today you need the DESCP returned (which is a 30-character text), and tomorrow you will need the opening inventory balance for this month (which is an 11,3 packed thingy). You can’t get both by using the same sub-procedure with return values because you have to put the parm length at the PR and PI top level in the sub-procedure. I suppose you could frog around with a default length on the PR and PI, then convert it when you got back to the calling program, but even then you would have to set it up within the sub-procedure, and that would require changes.

Plus, while the return values field is not in the prototype D-specs, you do reference the field name in both the calling and the called program, meaning you would need to modify them both if you changed the field you were having returned.

Fourth, and this is a big thing for me, it sort of hides the call. If a module is going to access another one, I like something that really stands out, like a CALLP. I agree, I still have the PR in my calling program, but using the eval versus a CALLP just hides it a bit as to where the access is occurring. I know a lot of people don’t have that hang-up, but what can you do? I am what I am.

In the end, the function call-return values combo is a valid alternative to the CALLP. However, except for the efficiency improvement, there are not enough positives to recommend it to me as my go-to access method. It’s OK, though, if you feel differently. What’s important, even if you don’t use it yourself, is that you can recognize it and understand how it works.

What Ya Shoulda Learned

In the end, there is a fair amount of stuff here, lots of rules, but there are really only a few things you want to make sure that you memorize. Other things can be looked up as needed.

  • First, understand the relationship between function calls and return That is, they are like a Reese’s Peanut Butter Cup: you don’t get one without the other (chocolate and peanut butter—gee, do I have to explain everything?).
  • Second, you need to know that you can only do the RETURN on one So if you need two fields returned, this approach by itself won’t work (it results in a compile error).
  • Third, you should have learned how to set up the caller and sub- procedure to return a variable other than a
  • You should also know how to set up a call to a sub-procedure as a function rather than by using the CALLP.

I am assuming there are other things you need to know, but I cannot relate any of them to function calls or return values.

Next time: The Importance of BIFs?  You can pick up Dave Shirey's book, 21st Century RPG: /Free, ILE, and MVC, at the MC Press Bookstore Today!

David Shirey

David Shirey is president of Shirey Consulting Services, providing technical and business consulting services for the IBM i world. Among the services provided are IBM i technical support, including application design and programming services, ERP installation and support, and EDI setup and maintenance. With experience in a wide range of industries (food and beverage to electronics to hard manufacturing to drugs--the legal kind--to medical devices to fulfillment houses) and a wide range of business sizes served (from very large, like Fresh Express, to much smaller, like Labconco), SCS has the knowledge and experience to assist with your technical or business issues. You may contact Dave by email at This email address is being protected from spambots. You need JavaScript enabled to view it. or by phone at (616) 304-2466.


MC Press books written by David Shirey available now on the MC Press Bookstore.

21st Century RPG: /Free, ILE, and MVC 21st Century RPG: /Free, ILE, and MVC
Boost your productivity, modernize your applications, and upgrade your skills with these powerful coding methods.
List Price $69.95

Now On Sale

BLOG COMMENTS POWERED BY DISQUS

LATEST COMMENTS

Support MC Press Online

$0.00 Raised:
$

Book Reviews

Resource Center

  • SB Profound WC 5536 Have you been wondering about Node.js? Our free Node.js Webinar Series takes you from total beginner to creating a fully-functional IBM i Node.js business application. You can find Part 1 here. In Part 2 of our free Node.js Webinar Series, Brian May teaches you the different tooling options available for writing code, debugging, and using Git for version control. Brian will briefly discuss the different tools available, and demonstrate his preferred setup for Node development on IBM i or any platform. Attend this webinar to learn:

  • SB Profound WP 5539More than ever, there is a demand for IT to deliver innovation. Your IBM i has been an essential part of your business operations for years. However, your organization may struggle to maintain the current system and implement new projects. The thousands of customers we've worked with and surveyed state that expectations regarding the digital footprint and vision of the company are not aligned with the current IT environment.

  • SB HelpSystems ROBOT Generic IBM announced the E1080 servers using the latest Power10 processor in September 2021. The most powerful processor from IBM to date, Power10 is designed to handle the demands of doing business in today’s high-tech atmosphere, including running cloud applications, supporting big data, and managing AI workloads. But what does Power10 mean for your data center? In this recorded webinar, IBMers Dan Sundt and Dylan Boday join IBM Power Champion Tom Huntington for a discussion on why Power10 technology is the right strategic investment if you run IBM i, AIX, or Linux. In this action-packed hour, Tom will share trends from the IBM i and AIX user communities while Dan and Dylan dive into the tech specs for key hardware, including:

  • Magic MarkTRY the one package that solves all your document design and printing challenges on all your platforms. Produce bar code labels, electronic forms, ad hoc reports, and RFID tags – without programming! MarkMagic is the only document design and print solution that combines report writing, WYSIWYG label and forms design, and conditional printing in one integrated product. Make sure your data survives when catastrophe hits. Request your trial now!  Request Now.

  • SB HelpSystems ROBOT GenericForms of ransomware has been around for over 30 years, and with more and more organizations suffering attacks each year, it continues to endure. What has made ransomware such a durable threat and what is the best way to combat it? In order to prevent ransomware, organizations must first understand how it works.

  • SB HelpSystems ROBOT GenericIT security is a top priority for businesses around the world, but most IBM i pros don’t know where to begin—and most cybersecurity experts don’t know IBM i. In this session, Robin Tatam explores the business impact of lax IBM i security, the top vulnerabilities putting IBM i at risk, and the steps you can take to protect your organization. If you’re looking to avoid unexpected downtime or corrupted data, you don’t want to miss this session.

  • SB HelpSystems ROBOT GenericCan you trust all of your users all of the time? A typical end user receives 16 malicious emails each month, but only 17 percent of these phishing campaigns are reported to IT. Once an attack is underway, most organizations won’t discover the breach until six months later. A staggering amount of damage can occur in that time. Despite these risks, 93 percent of organizations are leaving their IBM i systems vulnerable to cybercrime. In this on-demand webinar, IBM i security experts Robin Tatam and Sandi Moore will reveal:

  • FORTRA Disaster protection is vital to every business. Yet, it often consists of patched together procedures that are prone to error. From automatic backups to data encryption to media management, Robot automates the routine (yet often complex) tasks of iSeries backup and recovery, saving you time and money and making the process safer and more reliable. Automate your backups with the Robot Backup and Recovery Solution. Key features include:

  • FORTRAManaging messages on your IBM i can be more than a full-time job if you have to do it manually. Messages need a response and resources must be monitored—often over multiple systems and across platforms. How can you be sure you won’t miss important system events? Automate your message center with the Robot Message Management Solution. Key features include:

  • FORTRAThe thought of printing, distributing, and storing iSeries reports manually may reduce you to tears. Paper and labor costs associated with report generation can spiral out of control. Mountains of paper threaten to swamp your files. Robot automates report bursting, distribution, bundling, and archiving, and offers secure, selective online report viewing. Manage your reports with the Robot Report Management Solution. Key features include:

  • FORTRAFor over 30 years, Robot has been a leader in systems management for IBM i. With batch job creation and scheduling at its core, the Robot Job Scheduling Solution reduces the opportunity for human error and helps you maintain service levels, automating even the biggest, most complex runbooks. Manage your job schedule with the Robot Job Scheduling Solution. Key features include:

  • LANSA Business users want new applications now. Market and regulatory pressures require faster application updates and delivery into production. Your IBM i developers may be approaching retirement, and you see no sure way to fill their positions with experienced developers. In addition, you may be caught between maintaining your existing applications and the uncertainty of moving to something new.

  • LANSAWhen it comes to creating your business applications, there are hundreds of coding platforms and programming languages to choose from. These options range from very complex traditional programming languages to Low-Code platforms where sometimes no traditional coding experience is needed. Download our whitepaper, The Power of Writing Code in a Low-Code Solution, and:

  • LANSASupply Chain is becoming increasingly complex and unpredictable. From raw materials for manufacturing to food supply chains, the journey from source to production to delivery to consumers is marred with inefficiencies, manual processes, shortages, recalls, counterfeits, and scandals. In this webinar, we discuss how:

  • The MC Resource Centers bring you the widest selection of white papers, trial software, and on-demand webcasts for you to choose from. >> Review the list of White Papers, Trial Software or On-Demand Webcast at the MC Press Resource Center. >> Add the items to yru Cart and complet he checkout process and submit

  • Profound Logic Have you been wondering about Node.js? Our free Node.js Webinar Series takes you from total beginner to creating a fully-functional IBM i Node.js business application.

  • SB Profound WC 5536Join us for this hour-long webcast that will explore:

  • Fortra IT managers hoping to find new IBM i talent are discovering that the pool of experienced RPG programmers and operators or administrators with intimate knowledge of the operating system and the applications that run on it is small. This begs the question: How will you manage the platform that supports such a big part of your business? This guide offers strategies and software suggestions to help you plan IT staffing and resources and smooth the transition after your AS/400 talent retires. Read on to learn: