An ABL procedure file is a text file, with a .p extension, that contains source code for application procedures. The file itself is known as an external procedure, but it may contain within it other procedures, known as internal procedures. This topic contains information pertaining to ABL procedures.

  • External and internal procedures
  • Run a procedure
  • Return a value from a procedure
  • Use parameters with procedures
  • Persistent procedures
  • Organize procedure files

External and internal procedures

An ABL procedure (.p) file is known as an external procedure. It may accept parameters or return a string value. The following example code shows an external procedure, helloworld.p:

/* helloworld.p */ 

MESSAGE "Hello World".

An ABL internal procedure is another type of procedure that is defined by named entry points within an external procedure file. You define the start of an internal procedure using the PROCEDURE statement and indicate the end of the procedure using the END statement. You can have many internal procedures within the same external procedure file. The following example code has two internal procedures named proc1 and proc2 in the external procedure file, myprocedures.p.

/* myprocedures.p */

PROCEDURE proc1:
  /* ABL code */
  MESSAGE "In proc1".
END PROCEDURE.

PROCEDURE proc2:
  /* ABL code */
  MESSAGE "In proc2".
END PROCEDURE.

RUN proc1.
RUN proc2.

Scope of variables for internal procedures

You can define variables at a global level in the main block that can also be used by the internal procedures defined in the same file. Variables defined in the internal procedure, however, can only be used within the internal procedure.

Run a procedure

If you are running an external procedure file like helloworld.p, then you include the .p extension in the procedure name. You run internal procedures in almost exactly the same way that you run external procedures, except that there is no .p filename extension on the internal procedure name. The following example code shows running both an external and internal procedure:

/* myprocedures.p */

PROCEDURE proc1:
  /* ABL code */
  MESSAGE "In proc1".
END PROCEDURE.

PROCEDURE proc2:
  /* ABL code */
  MESSAGE "In proc2".
END PROCEDURE.

RUN helloworld.p.   // run external procedure
RUN proc1.     // run internal procedure

For more information, see Run ABL procedures in Develop ABL Applications:

Return a value from a procedure

You can use the RETURN statement to return execution to the caller, optionally specifying a string value to be returned. (Note that if you want to return a value other than a string, you need to use a FUNCTION, not a procedure.) In the caller you use the RETURN-VALUE function to retrieve the return value. Multiple RETURN statements within the same procedure are allowed. The following example code shows a return value being set in proc1, and the value retrieved in the caller:

/* myprocedures.p */

PROCEDURE proc1:
  /* ABL code */
  MESSAGE "In proc1".
  RETURN "1".

END PROCEDURE.

PROCEDURE proc2:
  /* ABL code */
  MESSAGE "In proc2".
END PROCEDURE.

RUN proc1.
MESSAGE "The return value from proc1 is" RETURN-VALUE.

For more information, see RETURN statement and RETURN-VALUE in Develop ABL Applications.

Use parameters with procedures

To pass values in or out of a procedure you can define parameters using the DEFINE PARAMETER statement. The parameter definition names the parameter, specifies whether it receives input or provide output, or both, and specifies the data type. This is the simplified syntax for the DEFINE PARAMETER statement:

DEFINE { INPUT | OUTPUT | INPUT-OUTPUT } PARAMETER parameter-name AS datatype [ NO-UNDO ] [ INITIAL initial-value ]
INPUT | OUTPUT | INPUT-OUTPUT
  • INPUT - value is passed in to the procedure you are running.
  • OUTPUT - value is returned to the caller when the called procedure completes.
  • INPUT-OUTPUT - value is passed in to the procedure, which can modify the value. The AVM passes the value back to the caller when the procedure ends.
parameter-name
The name of the parameter.
datatype
The datatype of the parameter. You can use any one of the standard ABL data types, including CHARACTER, INTEGER, DECIMAL, LOGICAL, DATE. You can also specify TABLE, DATASET, TABLE-HANDLE, or DATASET-HANDLE, but these use a slightly different syntax.
initial-value
The initial value of the parameter. This only applies to OUTPUT parameters.

You specify parameters in the RUN statement to pass a parameter from the calling procedure to the called procedure. When you specify the parameter, you also specify the kind of parameter you are using, (INPUT, OUTPUT, or INPUT-OUTPUT). INPUT is the default, but a best practice is to specify the kind of parameter so that your code is unambiguous to other developers.

You must ensure that when you call a procedure that takes parameters, the number and order of parameters match, as well as their ABL data types, and input/output types. In the calling procedure, when you use variables to pass values to and from a procedure, the names of the variables need not match the names used in the procedure.

For more details, see Parameter definition syntax and Parameter passing syntax.

The following example code demonstrates passing parameters:

VAR CHAR v1 = "XXX".
VAR CHAR v2 = "YYY".
VAR CHAR v3 = "ZZZ".

/* proc2 */
PROCEDURE proc2:
  DEFINE INPUT PARAMETER p1 AS CHARACTER NO-UNDO. 
  DEFINE OUTPUT PARAMETER p2 AS CHARACTER NO-UNDO.
  DEFINE INPUT-OUTPUT PARAMETER p3 AS CHARACTER NO-UNDO.

  p1 = "XXXXXX".  
  p2 = "YYYYYY".  
  p3 = "ZZZZZZ". 

  MESSAGE "In proc2: p1, p2, p3:" p1 p2 p3.
END PROCEDURE.

/* main */
MESSAGE "Before running proc2: v1, v2, v3:" v1 v2 v3.

RUN proc2 (INPUT v1, OUTPUT v2, INPUT-OUTPUT v3).

MESSAGE "After running proc2: v1, v2, v3:" v1 v2 v3.

The following output is produced from the example code:

Before running proc2: v1, v2, v3: XXX YYY ZZZ
In proc2: p1, p2, p3: XXXXXX YYYYYY ZZZZZZ
After running proc2: v1, v2, v3: XXX YYYYYY ZZZZZZ

Persistent procedures

A persistent procedure is an instance of a procedure that stays resident in the AVM's memory until it is explicitly deleted. You can use persistent procedures as a “memory cache” of frequently used procedures and functions. The procedure maintains its state. For example, if an internal procedure you call sets a variable at the .p (main block) level, that value stays set and can then be used from a different internal procedure.

Since the procedure stays in memory, the AVM does not have to load the program into memory each time it is called. Thus, there is a performance benefit to your application.

To instantiate a persistent procedure in memory, you use the RUN statement with the keyword PERSISTENT. You also define a handle to the procedure so that you access it later.

To instantiate the persistent procedure, use the following syntax:

RUN proc-name PERSISTENT SET proc-handle-name.

In the following example code, we define the procedure handle variable, hEmpLibrary and instantiate the persistent procedure setting the procedure handle variable. You must ensure that the procedure you run is in your PROPATH at runtime.

VAR HANDLE hEmpLibrary.
RUN emplibrary.p PERSISTENT SET hEmpLibrary.

Once the persistent procedure has been instantiated, you can run any of its internal procedures in the library. You use the procedure handle to reference its internal procedures.

RUN proc-name IN proc-handle-name [ (parameter-list) ].

In the following example code, we define the procedure handle variable, hEmpLibrary and instantiate the persistent procedure in the RUN statement. In the FOR EACH statement, we call the internal procedure calcvacation for every employee and display the information.

/* eCheckEmpVacAvailable.p */

VAR DATE dtStart.
VAR DATE dtEnd.
VAR LOGICAL lOK.
VAR HANDLE hEmpLibrary.

RUN emplibrary.p PERSISTENT SET hEmpLibrary.


FOR EACH Employee:
  lOK = FALSE.
  RUN calcvacation IN hEmpLibrary(INPUT EMPLOYEE.EMPNUM, INPUT dtStart, INPUT dtEnd, OUTPUT lOK).

  DISPLAY Employee.FirstName Employee.LastName lOK label "Vac?".
END.

Organize procedure files

There are multiple ways of organizing your procedure files. A good practice is to create procedure files of related functionality (libraries), which contain just internal procedures. You can then make these libraries available to other parts of your application. This promotes re-usability and makes it easier to maintain your application.

Another good practice is to separate user interface (UI) logic from business logic. The UI logic could be a mobile or web app, or an ABL GUI desktop client. Separating UI logic from business logic makes it easier to update the application because, in general, changes to one side of the application do not affect the other side. In a modern application, the UI logic does not directly access the database. The UI logic requests data from the business logic, which in turn, retrieves the data from the database. Exchanging data between the UI logic and the business logic is done using ABL temp-tables and datasets.