Try OpenEdge Now
skip to main content
ABL Reference
ABL Syntax Reference : FUNCTION statement
 

FUNCTION statement

Defines or declares a prototype for a user-defined function, or declares a Web service operation. The following syntax boxes describe the syntax for each use of the statement, beginning with a user-defined function definition.

Syntax

FUNCTION function-name [ RETURNS ] return-type [ PRIVATE ]
  [ ( parameter[ , parameter ] ... ) ] :

  function-body
Use the following syntax to declare a user-defined function prototype that is defined later in the same procedure or that is defined in another external procedure:
FUNCTION function-name [ RETURNS ] return-type
  [ ( parameter[ , parameter ] ... ) ]
  {    FORWARD
     | [ MAP [ TO ] actual-name ] IN proc-handle
     | IN SUPER
  } .
Use the following syntax to declare a Web service operation. For more information on declaring Web service operations, see OpenEdge Development: Web Services.
FUNCTION operationName [ RETURNS ] return-type
  [ ( parameter [ , parameter ] ... ) ]
 IN hPortType .
function-name
The name of the function. You must avoid ABL reserved keywords. For a list of ABL keywords, see the Keyword Index in this manual.
[ RETURNS ] return-type
Indicates the data type of the function return value. You can specify return-type as one of the following data types. For more information on each data type, see the Data types reference entry:
{ CHARACTER | COM-HANDLE | DATE | DATETIME | DATETIME-TZ
| DECIMAL | HANDLE | INT64 | INTEGER | LOGICAL | LONGCHAR
| MEMPTR | RAW  | RECID | ROWID
  | [ CLASS ] object-type-name } [ EXTENT [ constant ] ]
object-type-name
Specifies the type name of an ABL or .NET class or interface. Specify an object type name using the syntax described in the Type-name syntax reference entry. With an appropriate USING statement, you can also specify a class or interface name alone, without the qualifying package or namespace.
CLASS
If the specified class or interface type name conflicts with an abbreviation of a built-in primitive type name, such as INT for INTEGER, you must specify the CLASS keyword.
For a class or interface return value, ABL returns an object reference associated with the class or interface, not a class instance itself. For more information on object references, see the Class-based object reference reference entry.
EXTENT [constant ]
Defines the return value as an array of data elements with the specified primitive or object type. This option can specify an array return value as either determinate (has a defined number of elements) or indeterminate (has an undefined number of elements). To define a determinate array return value, specify the EXTENT option with the constant argument. This optional argument is an integer value that represents the number of elements in the array. To define an indeterminate array return value, specify the EXTENT option without the constant argument.
An indeterminate array return value can be in one of two states: fixed or unfixed, meaning it either has a fixed dimension or it does not. An indeterminate array return value has an unfixed dimension when first defined. You can fix the dimension of an indeterminate array return value by:
*Setting the number of elements in the array return value using the EXTENT statement
*Assigning a determinate array to the indeterminate array value, fixing it to the dimension of the determinate array
*Passing array parameters to a procedure, user-defined function, or class-based method, so that the indeterminate array value is the target for the passing of a determinate array, fixing the indeterminate array to the dimension of the determinate array
Once fixed, ABL treats a fixed indeterminate array as a determinate array.
If you do not use the EXTENT option (or you specify constant as 0), the return value is not an array return value.
Note: If you invoke a function on an AppServer, the function cannot return a value as a LONGCHAR, MEMPTR, or CLASS.
PRIVATE
Indicates the following about the user-defined function:
*That it cannot be invoked from an external procedure—that is, from a procedure file external to the current procedure file.
*That the INTERNAL-ENTRIES attribute on the procedure that defines it does not provide its name (unless the procedure that defines it is the current procedure file).
*That the GET-SIGNATURE( ) method on the procedure that defines it does not provide its signature (unless the procedure that defines it is the current procedure file).
( parameter [ , parameter ] ... )
Defines one or more parameters of the function.
For information on the parameter definition syntax, see the Parameter definition syntax reference entry.
function-body
The body of a function definition. Define function-body using the following syntax:
function-logic
.
.
.
    [ catch-block [ catch-block ... ] ]
    [ finally-block ]
END [ FUNCTION ] .
function-logic
The logic of the function. This logic can contain the ABL statements allowed within a procedure block, except that the RETURN ERROR statement returns the Unknown value (?) for the function, regardless of its return type, but does not raise ERROR in the caller.
To return a function value of the data type specified by return-type, you can execute the RETURN statement to set a value of that data type to return at run time. If you omit the RETURN statement, the function returns the Unknown value (?), regardless of the data type specified by return-type.
If return-type is defined as a .NET array of mapped types (for example, "System.Byte[]"), you must return an object reference of the specified .NET array of mapped types in the RETURN statement. You cannot return an ABL array of a type that maps to the .NET array type (for example, INTEGER EXTENT) or the AVM raises a run-time error. If you do not execute any RETURN statement for return-type in the function-logic, the user-defined function returns the Unknown value (?) as its return value.
Each logic statement must end with a period.
catch-block
Specifies a CATCH statement that defines error handling code for one or more error types. A DO block does not have any default error handling. Therefore, a DO block must have error handling options specified such that it becomes an undoable block. Otherwise, ABL generates a compiler warning. For more information on catch-block, see the CATCH statement reference entry.
finally-block
Specifies a FINALLY statement that defines the processing that must occur after all other processing in the block occurs. For more information on finally-block, see the FINALLY statement reference entry.
END [ FUNCTION ]
Specifies the end of the function body definition. You must end the function body definition with the END statement.
FORWARD
Declares a prototype for a function in a procedure whose definition appears later in the same procedure. You must declare a user-defined function prototype when the function definition appears within the same procedure following the first use of the function. This prototype must appear in the procedure before the first use of the function.
The FUNCTION statement with the FORWARD option must include the following information on the function: the data type it returns, and the data type and mode (INPUT, OUTPUT, or INPUT-OUTPUT) of each parameter.
If you declare a function prototype, reference it, and do not define it before the end of the procedure, the compiler returns an error.
[ MAP [ TO ]actual-name] IN proc-handle
Declares prototype for a function that resides in a procedure external to the declaring procedure, with the following information:
*Optionally, that function-name (the second element in the FUNCTION statement) is an alias (alternative name) for the function and that actual-name is the name that appears in the function definition.
*The definition of the function resides in another external procedure specified by proc-handle, which represents an expression that evaluates to a handle to the procedure that defines the function. This procedure can be an active procedure in the local context or in a remote persistent, single-run, or singleton procedure. For more information on remote user-defined functions, see OpenEdge Application Server: Developing AppServer Applications.
Note: The MAP option might simplify your code if it references two different user-defined functions that have the same name but that reside in different procedures.
A FUNCTION statement with the IN proc-handle option must include the following information on the function: the data type it returns, and the data type and mode (INPUT, OUTPUT, or INPUT-OUTPUT) of each parameter.
IN SUPER
Declares a prototype for a function whose definition resides in a super procedure.
A FUNCTION statement with the IN SUPER option must include the following information on the function: the data type it returns, and the data type and mode (INPUT, OUTPUT, or INPUT-OUTPUT) of each parameter.
operationName
The name of a Web service operation specified in a WSDL file.
hPortType
A handle to a procedure object that encapsulates a Web service operation.

Examples

The first example, r-udf1.p, defines and references the user-defined function doubler( ), which accepts an integer and returns the integer multiplied by two:
r-udf1.p
/* r-udf1.p */
/* Defines and references a user-defined function */

/* Define doubler() */
FUNCTION doubler RETURNS INTEGER (INPUT parm1 AS INTEGER):
  RETURN (2 * parm1).
END FUNCTION.

/* Reference doubler() */
DISPLAY  "doubler(0)=" doubler(0) SKIP
  "doubler(1)=" doubler(1) skip
  "doubler(2)=" doubler(2) skip.
The second example, r-udf2.p, declares a prototype for, references, and defines doubler( ):
r-udf2.p
/* r-udf2.p */
/* Forward-declares, references, and defines a user-defined function */

/* Forward declare doubler() */
FUNCTION doubler RETURNS INTEGER (INPUT parm1 AS INTEGER) FORWARD.

/* Reference doubler() */
DISPLAY "doubler(0)=" doubler(0).
DISPLAY "doubler(1)=" doubler(1).
DISPLAY "doubler(2)=" doubler(2).

/* Define doubler() */
FUNCTION doubler RETURNS INTEGER (INPUT parm1 AS INTEGER):
  RETURN (2 * parm1).
END FUNCTION.
The third example consists of two procedures, r-udf3.p and r-udfdef.p. The example illustrates defining a prototype for user-defined function that is defined in an external procedure.
The procedure, r-udf3.p, declares the prototype for doubler( ), runs r-udfdef.p persistently, invokes doubler( ), and deletes the persistent procedure:
r-udf3.p
/* r-udf3.p */
/* References an externally-defined user-defined function */

/* Define items */
DEFINE VARIABLE myhand AS HANDLE    NO-UNDO.
DEFINE VARIABLE mystr  AS CHARACTER NO-UNDO FORMAT "x(20)".

/* Forward declare doubler() */
FUNCTION doubler RETURNS INTEGER (INPUT parm1 AS INTEGER) IN myhand.

/* Run the procedure that defines doubler() */
RUN src\prodoc\langref\r-udfdef.p PERSISTENT SET myhand.
    
/* Reference doubler() */
DISPLAY "doubler(0)=" doubler(0) SKIP
        "doubler(1)=" doubler(1) SKIP
        "doubler(17)=" doubler(17) SKIP.

/* Delete the procedure that defines doubler */
DELETE PROCEDURE myhand.
The second procedure, r-udfdef.p, defines doubler( ):
r-udfdef.p
/* r-udfdef.p */
/* Defines user-defined function doubler() */

FUNCTION doubler RETURNS INTEGER (INPUT parm1 AS INTEGER):
  RETURN (2 * parm1).
END FUNCTION.
To start the third example, run r-udf3.p in the Procedure Editor.
In the fourth example, r-fctrl2.p, the user-defined function fact( ) implements the factorial function, common in probability and statistics, and commonly notated "!" (6! = 6 x 5 x 4 x 3 x 2 x 1; 100! = 100 x 99 x 98 x ... x 3 x 2 x 1):
r-fctrl2.p
/* r-fctrl2.p */
/* Demonstrates user-defined function fact() */

DEFINE VARIABLE inp AS INTEGER LABEL "Input Value".

FUNCTION fact RETURNS INTEGER (INPUT val AS INTEGER):
  IF val LT 0 THEN RETURN 0.
  IF val LE 1 THEN RETURN 1.
  RETURN val * fact(val - 1).
END.

REPEAT:
  UPDATE inp WITH TITLE "Factorials".
  DISPLAY fact(inp) LABEL "Factorial".
END.

Notes

*You can terminate a FUNCTION statement with either a period (.) or a colon (:), but typically use a colon (:) for a function definition and a period (.) for a function prototype or to declare a Web service operation.
*Before you reference a user-defined function within a procedure, you must define it, declare its prototype, declare it as external (by using FUNCTION statement's IN option), or define it.
*You cannot define shared objects, work tables, temp-tables, or ProDataSet objects within a user-defined function.
*ABL implements scalar and array parameters of user-defined functions as NO-UNDO variables.
*A reference to a user-defined function must match the declared prototype or definition with respect to the return type, and with respect to the number, type, and mode of the parameters.
*When an ABL predicate (such as a WHERE clause) contains a user-defined function, the AVM evaluates the function once—when it opens the query or enters the FOR EACH block.
*When the AVM encounters a user-defined function declared externally that references a user-defined function declared externally that references a user-defined function declared externally, etc., the AVM tolerates up to 64 levels of indirection. At the 65th level, the AVM raises an error and returns the Unknown value (?).
*If a user-defined function has one or more buffer parameters and its definition resides in another procedure, the referencing procedure and the defining procedure must reside on the same machine. If a user-defined function does not have buffer parameters, the invoking procedure and the defining procedure can reside on different machines.
*When you invoke a user-defined function (or a built-in function), you do not need to assign the function's return value to a variable. That is, you can invoke a user-defined function as a statement, ignoring the return value. You might use this technique with a function that performs some action on a persistent object, such as a shared variable, when you want the action to occur and do not need to check the return value. For example:
doubler(my-shared-variable).
*When you invoke a user-defined function, you may pass a TABLE, TABLE-HANDLE, DATASET, or DATASET-HANDLE parameter by value, by reference, or by binding using the BY-VALUE, BY-REFERENCE, or BIND keyword, respectively. For example:
myfunc(OUTPUT TABLE tt BY-REFERENCE).
For more information about passing these parameters by value, by reference, or by binding, see the Parameter passing syntax reference entry.
*To return an error to the caller from a user-defined function, you can:
*Migrate from using a user-defined function defined within a procedure to using a method defined within a class.
*Use the ROUTINE-LEVEL ON ERROR UNDO, THROW statement in conjunction with the THROW option of the UNDO statement or the ON ERROR phrase in the user-defined function block.
*Use the THROW option of the UNDO statement or the ON ERROR phrase from a CATCH block (CATCH statement) within the user-defined function block.
*Invoke the STOP statement to raise the STOP condition in the caller.

See also

DYNAMIC-FUNCTION function, METHOD statement, Parameter definition syntax, PROCEDURE statement, RETURN statement