An introduction to free-form RPG

Log in to save

Anyone who learned RPG by counting columns has an understandable reflex when faced with free-form: it looks like a different language. It is not. The operations are the same, files are read the same way, programs compile with the same command. What changes is where you write things, and a few habits the column format imposed.

**FREE and the end of positions

From 7.1 with PTFs, and natively from 7.2, a source member can begin with:

**free

From that point the compiler stops caring about columns: you write from position 1 to 80 as in any other language, with whatever indentation you prefer.

That is a deeper difference than it sounds. In the column format indentation was decorative, because position was what counted; here indentation becomes the only way to show structure, exactly as in Java or Python.

Declarations

The old D specifications become readable statements:

dcl-s counter int(10);
dcl-s amount  packed(11:2);
dcl-s name    char(30);
dcl-s today   date;

dcl-ds customer qualified;
  code    packed(7:0);
  company char(40);
  balance packed(11:2);
end-ds;

qualified on data structures deserves attention: it forces you to write customer.code instead of code. It looks wordier, but it removes at the root any collision between subfield names of different structures — a problem that costs hours in long programs.

Files are declared with dcl-f:

dcl-f ORDERS  usage(*input) keyed;
dcl-f REPORT  printer;

Procedures in place of subroutines

This is the change that brings the biggest benefit. A BEGSR/ENDSR subroutine works on the program's global variables: whatever it touches, it touches for everyone. A procedure has parameters, a return value and its own variables:

dcl-proc calculateDiscount;
  dcl-pi *n packed(5:2);
    netAmount packed(11:2) const;
    category  char(1)      const;
  end-pi;

  dcl-s percentage packed(5:2);

  select;
    when category = 'A';
      percentage = 15;
    when category = 'B';
      percentage = 10;
    other;
      percentage = 0;
  endsl;

  return netAmount * percentage / 100;
end-proc;

You call it like any other function: discount = calculateDiscount(total : custCategory);.

const on parameters says two things at once: that the procedure will not modify that value, and that the caller may pass an expression instead of a variable. It is a small declaration that makes the code much easier to use.

Indicators can be left behind

In the column format, indicators were the only way to know how an operation had gone. In free-form there are built-in functions:

// Instead of reading indicator 90
read ORDERS;
dow not %eof(ORDERS);
  // processing
  read ORDERS;
enddo;

chain (customerCode) CUSTOMERS;
if %found(CUSTOMERS);
  // found
endif;

%eof, %found and %error say the same thing *IN90 and friends said, but they say it in words. When you reopen the program six months later, you notice the difference.

What has not changed

The RPG cycle still exists, but new programs barely use it: you write dcl-f ... usage(*input) and control the flow yourself. H specs have become ctl-opt:

ctl-opt dftactgrp(*no) actgrp(*caller)
        option(*srcstmt: *nodebugio)
        datedit(*dmy/);

dftactgrp(*no) is close to mandatory in new programs: without it you cannot bind service programs or use most of ILE.

How to start without rewriting everything

There is no need to convert the existing estate. The calmest way is:

  1. Write only new programs in free-form.
  2. When you touch an old program, move to free-form only the part you are working on — the two formats can live in the same source, as long as the free-form sits inside /free ... /end-free in column-format sources.
  3. As useful procedures accumulate, gather them into a service program instead of copying them from one program to another.

Point 3 is what changes the way you work most over time, and it is also why the move is worth making: free-form on its own is convenience, procedures and service programs are a different way of organising the work.


Which versions this works on

**FREE (fully free-form, declarations included) — introduced natively with IBM i 7.2, and available on 7.1 from Technology Refresh 7 with the RPG compiler PTF SI51094 or a later supersede. On 7.1 without that PTF, **FREE is not recognised.

Free-form calculation specifications (/free ... /end-free) are far older: they were already there in V5R1. That is why on older systems you often meet mixed code — columns for declarations, free-form only for calculations. If you are on a release earlier than 7.1, that remains the way:

     D counter         S             10I 0
     D amount          S             11P 2
     C                   eval      counter = 0
      /free
       counter = counter + 1;
      /end-free

The free-form declarations dcl-s, dcl-ds, dcl-f, dcl-proc arrive together with fully free-form: same availability, 7.1 TR7 with the PTF or 7.2 onwards.

dcl-f with usage(*input), %eof, %found — the built-in functions long predate free-form and can be used in the column format too.

Sources

← Back to blog

Comments

No comments yet. Be the first to comment!

You need an account to comment. Log in · Sign up