User-Defined Functions and Prototypes

This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production.

The reference material consulted while preparing these pages is listed in the section bibliography.

This page is written against ISO/IEC 1989:2023 (edition 3, January 2023; an upcoming 4th edition is in ballot), in which user-defined functions come from COBOL 2002 and function prototypes from COBOL 2014; against IBM Enterprise COBOL for z/OS 6.5 (PTFs through September 2026) on z/OS 3.2, where FUNCTION-ID arrived in 6.4 and function prototypes followed in 6.4 as a PTF; and against GnuCOBOL 3.2 (28 July 2023, the latest release), with which every program on it was compiled with cobc -x -Wall and run on 2026-10-20 (and again with -std=ibm where it differs). z/OS examples are labelled "z/OS; not run here" and checked against the Language Reference and the Programming Guide.

A user-defined function is a piece of COBOL that you write once and use the way you use an intrinsic function: in the middle of an expression or as an operand, with arguments in parentheses and one returned value. The Circulation system has a rule that a program needs in several places, the overdue fine for a number of days, and a function lets COMPUTE, ADD and MOVE use it directly instead of CALL`ing a subprogram and then using a work field. This page covers the definition (`FUNCTION-ID, RETURNING), the invocation (REPOSITORY, the FUNCTION keyword), how arguments are passed, separate compilation and function prototypes, the type checking that prototypes enable, and when a subprogram is still the better choice. The functions that the language provides are on Intrinsic Functions; the CALL statement, the parameter-passing modes and the LINKAGE SECTION are on Subprograms, CALL and Linkage; the REPOSITORY paragraph in its place in the program is on Program Structure and Divisions; COPY is on Copybooks and REPLACE.

Where the book is outdated: Taulli’s Modern Mainframe Development (2022, built around Enterprise COBOL 6.3) does not mention user-defined functions or prototypes at all; FUNCTION-ID and function prototypes first appear in Enterprise COBOL 6.4, which came after the release the book calls the latest. Everything on this page therefore comes from the official manuals. The book’s chapter 7 on CALL is the subject of the subprograms page.

What a user-defined function is

A function is a separate compilation unit, like a program, but with its own header and trailer and a returned value:

  • It starts with FUNCTION-ID instead of PROGRAM-ID and ends with END FUNCTION name.

  • It is never nested: a function cannot be written inside a program, another function, a method or a class.

  • Its PROCEDURE DIVISION header must have a RETURNING phrase, which names the data item that holds the result. USING is optional and lists zero or more parameters.

  • It is called by writing its name where an operand is allowed, not by the CALL statement. Enterprise COBOL says that a function reached through CALL behaves unpredictably, and that pointers to user-defined functions are not supported.

  • In Enterprise COBOL, a function is always recursive and may invoke itself, and the CALLINTERFACE directive does not affect it; its linkage is chosen by ENTRY-INTERFACE (STATIC by default, DYNAMIC or DLL).

  • The function name follows the rules of a program name: at most 30 characters, letters, digits, hyphens and underscores, at least one letter, and not starting or ending with a hyphen. Intrinsic function names can be reused as user-defined names, except LENGTH, RANDOM, SIGN, SUM and WHEN-COMPILED.

The complete header, as the Language Reference gives it, is the following. AS 'literal' gives an external name that differs from the COBOL name, and ENTRY-NAME and ENTRY-INTERFACE are Enterprise COBOL paragraphs (the PGMNAME option applies to the external name when AS is given).

IDENTIFICATION DIVISION.
FUNCTION-ID. function-name [AS 'literal'].
    [ENTRY-NAME IS {COMPAT | LONGUPPER | LONGMIXED}.]
    [ENTRY-INTERFACE IS {STATIC | DYNAMIC | DLL}.]
ENVIRONMENT DIVISION.   (optional)
DATA DIVISION.          (optional; LINKAGE SECTION for the parameters)
PROCEDURE DIVISION [USING [BY REFERENCE | BY VALUE] parameter ...]
                   RETURNING result-item.
    statements ... GOBACK.
END FUNCTION function-name.

A first function: the overdue fine

The next compilation group holds the function OVERDUE-FINE and a program that uses it. It implements the library’s rule: 0.35 per day, capped at 5.00. The parameter and the result are LINKAGE SECTION items at level 01, the work field is in LOCAL-STORAGE (so each invocation starts clean), and GOBACK ends the function. The function comes first in the file because the compiler must have seen it before the program that invokes it; the program then names it in REPOSITORY, which allows it to be written without the FUNCTION keyword.

       IDENTIFICATION DIVISION.
       FUNCTION-ID. OVERDUE-FINE.
      * Fine for a number of days overdue: 0.35 per day, capped at 5.00.
       DATA DIVISION.
       LOCAL-STORAGE SECTION.
       01  LS-FINE             PIC 9(3)V99.
       LINKAGE SECTION.
       01  L-DAYS              PIC 9(3).
       01  L-FINE              PIC 9(3)V99.
       PROCEDURE DIVISION USING L-DAYS RETURNING L-FINE.
           COMPUTE LS-FINE = L-DAYS * 0.35
           IF LS-FINE > 5.00
               MOVE 5.00 TO LS-FINE
           END-IF
           MOVE LS-FINE TO L-FINE
           GOBACK.
       END FUNCTION OVERDUE-FINE.

       IDENTIFICATION DIVISION.
       PROGRAM-ID. FINEFN.
       ENVIRONMENT DIVISION.
       CONFIGURATION SECTION.
       REPOSITORY.
           FUNCTION OVERDUE-FINE.
       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01  WS-DAYS             PIC 9(3).
       01  WS-TOTAL            PIC 9(3)V99 VALUE ZERO.
       01  WS-SHOW             PIC ZZ9.99.
       PROCEDURE DIVISION.
           PERFORM VARYING WS-DAYS FROM 3 BY 7 UNTIL WS-DAYS > 20
               MOVE OVERDUE-FINE(WS-DAYS) TO WS-SHOW
               DISPLAY WS-DAYS " days: " WS-SHOW
               ADD OVERDUE-FINE(WS-DAYS) TO WS-TOTAL
           END-PERFORM
           MOVE WS-TOTAL TO WS-SHOW
           DISPLAY "total: " WS-SHOW
           MOVE FUNCTION OVERDUE-FINE(WS-DAYS) TO WS-SHOW
           DISPLAY "with the FUNCTION keyword: " WS-SHOW
           STOP RUN.
       END PROGRAM FINEFN.

Compiled with cobc -x -Wall finefn.cob (and with -std=ibm), no warnings, it prints:

003 days:   1.05
010 days:   3.50
017 days:   5.00
total:   9.55
with the FUNCTION keyword:   5.00

The cap shows at 17 days (5.95 is cut to 5.00). The last line is for 24 days, the value WS-DAYS holds after the loop. The function is not a program, so cobc -x still makes FINEFN the executable although the function comes first in the file; the function is compiled into the same object and found by name.

Invoking a function: REPOSITORY and the FUNCTION keyword

A user-defined function is invoked like an intrinsic one, and in most places where one is allowed: as an operand of MOVE, COMPUTE, ADD, IF, DISPLAY, inside arithmetic expressions and as an argument of another function. It cannot be a receiving operand. There are two ways to write the reference:

  • With the keyword: FUNCTION OVERDUE-FINE(WS-DAYS). Nothing else is needed in the invoking program, but the compiler must still know the function (a definition or a prototype earlier in the compilation group).

  • Without the keyword: OVERDUE-FINE(WS-DAYS), after the name is declared in the REPOSITORY paragraph of the CONFIGURATION SECTION: FUNCTION OVERDUE-FINE. The name then cannot be used as an ordinary user-defined word in that scope. Several functions can follow one FUNCTION word or be written as separate entries; FUNCTION ALL INTRINSIC and FUNCTION name INTRINSIC declare intrinsic functions the same way. A REPOSITORY paragraph is allowed only in the outermost program, and applies to the programs contained in it.

A function with no parameters is written without parentheses in Enterprise COBOL (MOVE FUNCTION GetRecord TO A in IBM’s example). Without REPOSITORY and without the keyword the name is just an undefined word, and cobc rejects the program:

n2.cob:7: error: FUNCTION 'NO-SUCH-FN' unknown
n2.cob:8: error: 'NO-SUCH-FN' is not defined

The first line is the reference with FUNCTION to a name that no definition or prototype provides, the second is the reference without it. A function that has no RETURNING clause is a compile-time error in both compilers; GnuCOBOL says error: RETURNING clause is required for a FUNCTION.

A reference to a function whose definition or prototype the compiler has not seen is accepted but reported when the -Wprototypes warning (included in -Wall) is on. It is the usual sign that the function is defined later in the file or in another file:

n3.cob:6: warning: no definition/prototype seen for FUNCTION 'OVERDUE-FINE' [-Wprototypes]

Parameters and the result

The parameters are listed in PROCEDURE DIVISION USING, each a level-01 or level-77 item of the LINKAGE SECTION, and the correspondence with the arguments is by position, never by name. The number of arguments must equal the number of parameters. Two passing modes exist for functions:

  • BY REFERENCE (the default). The function works on the caller’s storage, as with CALL: a change to the parameter changes the argument.

  • BY VALUE. The function gets a copy. In Enterprise COBOL the parameter must then be a single binary item, floating-point item, pointer, function pointer, procedure pointer, one single-byte character or one national character; a group item cannot be passed this way.

  • There is no BY CONTENT for the formal parameters of a function. To make an argument arrive as a copy, declare the parameter BY REFERENCE and wrap the argument at the call in the intrinsic function CONTENT-OF, as in OVERDUE-FINE(CONTENT-OF(WS-DAYS)). CONTENT-OF returns a temporary copy of the same size, and the function modifies the copy.

  • The result. RETURNING names a level-01 or level-77 LINKAGE SECTION item; its value when the function returns is the value of the function. The item is output only: its starting value is undefined, so the function must set it before it reads it. An unbounded group cannot be the result.

An argument can be any data item of the caller, whatever its level and whichever section defines it, and in Enterprise COBOL a literal or an arithmetic expression is allowed as an argument to a function. That is where the portable behaviour and the compiler’s checking part ways, as the next two programs show. IBM’s own example passes the literals "add", 10 and 0.23 to parameters described as PIC X(3), PIC 999 and PIC V999, and prints 010230 (z/OS output, as printed in the Programming Guide). The same function in GnuCOBOL 3.2, with literals and with data items:

       IDENTIFICATION DIVISION.
       FUNCTION-ID. DOCALC.
       DATA DIVISION.
       LINKAGE SECTION.
       01  KIND                PIC X(3).
       01  ARG-A               PIC 999.
       01  ARG-B               PIC V999.
       01  RES                 PIC 999V999.
       PROCEDURE DIVISION USING BY REFERENCE KIND ARG-A ARG-B
                          RETURNING RES.
           IF KIND = "add"
               COMPUTE RES = ARG-A + ARG-B
           END-IF
           GOBACK.
       END FUNCTION DOCALC.

       IDENTIFICATION DIVISION.
       PROGRAM-ID. MAINPROG.
       ENVIRONMENT DIVISION.
       CONFIGURATION SECTION.
       REPOSITORY.
           FUNCTION DOCALC.
       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01  RESULT              PIC 999V999 USAGE DISPLAY.
       01  A1                  PIC X(3) VALUE "add".
       01  A2                  PIC 999 VALUE 10.
       01  A3                  PIC V999 VALUE 0.23.
       PROCEDURE DIVISION.
           COMPUTE RESULT = DOCALC("add" 10 0.23)
           DISPLAY "literals: " RESULT
           COMPUTE RESULT = DOCALC(A1 A2 A3)
           DISPLAY "items:    " RESULT
           GOBACK.
       END PROGRAM MAINPROG.

Compiled with cobc -x -Wall docalc.cob, no warnings, it prints:

literals: 100.023
items:    010.230

and with -std=ibm the same two results print without the decimal point (100023 and 010230). The data items give the expected 10.230. The literals do not: GnuCOBOL passes each literal at its own size (10 is two bytes, 0.23 is three) to parameters that read three bytes each, so the function reads past the argument and the result is wrong, with no diagnostic. Pass data items whose description is the parameter’s, or MOVE the literal to such an item first. IBM’s documentation shows the correct result for the same call on z/OS.

The same GnuCOBOL release has an intrinsic function named CONTENT-OF that is not the IBM one: it takes a pointer, not a data item. Its configuration file for -std=ibm lists the name as an IBM extension "different than GnuCOBOL one", and OVERDUE-FINE(CONTENT-OF(WS-DAYS)) is rejected by cobc 3.2 with error: FUNCTION 'CONTENT-OF' has invalid argument. To keep the copy semantics in portable code, MOVE the argument to a work field first.

Function prototypes

A function prototype describes a function without containing it: its name, its parameters and its result, and nothing else. It is a compilation unit of its own that produces no object code. With it the compiler can invoke a function that is written in another file (or is not even COBOL, such as a C function), check every invocation against the declaration, and generate the call for the right linkage. IBM’s Programming Guide recommends this arrangement: put the function in its own source file and have the callers use a prototype. Without a prototype the definition must come first in the same compilation group, and the link-edit then needs a binder ENTRY statement so that the program, not the function, is the entry point of the load module (ENTRY CELQSTRT for LP(64)).

The prototype is written as the function definition with IS PROTOTYPE in the FUNCTION-ID paragraph, its LINKAGE SECTION holds only the parameters and the result, and its PROCEDURE DIVISION header has USING and RETURNING and nothing after it. These are the rules from the Language Reference:

  • RETURNING is required; the division has no statements, declaratives or paragraphs.

  • A prototype has no REPOSITORY paragraph, WORKING-STORAGE, LOCAL-STORAGE or FILE SECTION; only the LINKAGE SECTION items that describe parameters and result are allowed, and they have no run-time representation.

  • A prototype cannot be nested, and it must come before the first invocation in the compilation group; the recommended order is prototypes, then function definitions, then programs.

  • AS 'literal' on the prototype gives the external name, so a COBOL name such as GetRecord can call a function that the operating environment knows as QQGETREC; ENTRY-NAME IS LONGMIXED allows a mixed-case external name; ENTRY-INTERFACE chooses STATIC, DYNAMIC or DLL linkage for the generated call.

  • The prototype is best kept in a copybook and brought in with COPY by every program that invokes the function and by the file that defines it, so that the compiler can check the definition against the prototype and the callers against the same text.

The Circulation prototype, as a copybook for Enterprise COBOL (z/OS; not run here), with the function that implements it and a caller. The caller declares CONTENT-OF and the function in REPOSITORY:

      * FINEPROT - prototype of OVERDUE-FINE
       IDENTIFICATION DIVISION.
       FUNCTION-ID. OVERDUE-FINE IS PROTOTYPE.
       DATA DIVISION.
       LINKAGE SECTION.
       01  P-DAYS              PIC 9(3).
       01  P-FINE              PIC 9(3)V99.
       PROCEDURE DIVISION USING P-DAYS RETURNING P-FINE.
       END FUNCTION OVERDUE-FINE.
      * OVERDUE: the function, with the prototype copied first
       COPY FINEPROT.
       IDENTIFICATION DIVISION.
       FUNCTION-ID. OVERDUE-FINE.
       DATA DIVISION.
       LOCAL-STORAGE SECTION.
       01  LS-FINE             PIC 9(3)V99.
       LINKAGE SECTION.
       01  L-DAYS              PIC 9(3).
       01  L-FINE              PIC 9(3)V99.
       PROCEDURE DIVISION USING L-DAYS RETURNING L-FINE.
           COMPUTE LS-FINE = L-DAYS * 0.35
           IF LS-FINE > 5.00
               MOVE 5.00 TO LS-FINE
           END-IF
           MOVE LS-FINE TO L-FINE
           GOBACK.
       END FUNCTION OVERDUE-FINE.
      * FINERPT: the caller, with the same prototype
       COPY FINEPROT.
       IDENTIFICATION DIVISION.
       PROGRAM-ID. FINERPT.
       ENVIRONMENT DIVISION.
       CONFIGURATION SECTION.
       REPOSITORY.
           FUNCTION OVERDUE-FINE
           FUNCTION CONTENT-OF INTRINSIC.
       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01  WS-DAYS             PIC 9(3) VALUE 10.
       01  WS-SHOW             PIC ZZ9.99.
       PROCEDURE DIVISION.
           MOVE OVERDUE-FINE(CONTENT-OF(WS-DAYS)) TO WS-SHOW
           DISPLAY WS-DAYS " days: " WS-SHOW
           GOBACK.
       END PROGRAM FINERPT.

The two sources are compiled separately, so no binder ENTRY statement is needed for a function that comes first: OVERDUE is link-edited into a load module of its own, and FINERPT is link-edited with it (static linkage, the default) or finds it at run time if ENTRY-INTERFACE IS DYNAMIC is added to both the prototype and the function. SQL, CICS and Java interoperability (JAVAIOP) cannot be used inside a user-defined function in Enterprise COBOL, because they need the definition to come ahead of the program using them; a function that must touch Db2 or CICS is a subprogram. A compiler fix (APAR PH69113) corrected a false error about LOCAL-STORAGE in a prototype when the SQL or SQLIMS option was on; the PTF is the cure, and NOSQL was the workaround.

Prototypes in GnuCOBOL 3.2

GnuCOBOL 3.2 accepts the same prototype syntax, but it marks it as unfinished. The same two files, built as a separate module and an executable (with -I. so that COPY finds the copybook; the prototype, function and caller are the ones above without the CONTENT-OF wrapper, and the caller loops over 3, 10 and 17 days), compile with one warning per file:

FINEPROT.CPY:3: warning: handling of FUNCTION PROTOTYPE is unfinished; implementation is likely to be changed [-Wunfinished]

That is the only message: the no definition/prototype seen warning of the previous sections is gone, because the prototype now provides the declaration. The module has to be named after the function with the hyphen written as two underscores, the name that the run time looks for. With cobc -m -I. -o OVERDUE__FINE.so overdue.cob and cobc -x -I. finerpt.cob, the run prints:

003 days:   1.05
010 days:   3.50
017 days:   5.00

Without the module of that name, or with another name for it, the run stops with libcob: error: user-defined FUNCTION 'OVERDUE__FINE' not found. Compiling both sources into one executable (cobc -x -fstatic-call finerpt.cob overdue.cob) gives the same output with no module file at all.

Type checking: what a prototype buys

With CALL, the compiler does not know the called program, so nothing is checked: the standard rule is that corresponding parameters have the same number of bytes, and a mismatch is found at run time, if ever. A function that the compiler knows (from its definition, or from a prototype) is different. Enterprise COBOL performs conformance checking at compile time on every invocation: the arguments are the senders and the formal parameters the receivers, and the rules are these.

  • The number of arguments must equal the number of parameters.

  • For elementary items passed BY REFERENCE, the parameter and the argument must agree in PICTURE, USAGE, SIGN, JUSTIFIED, BLANK WHEN ZERO and DYNAMIC LENGTH. The exceptions: an alphanumeric, national or UTF-8 argument may be longer than the parameter, currency symbols match only when the currency strings are equal, and the period and comma symbols match only when DECIMAL-POINT IS COMMA is the same on both sides.

  • For group items passed BY REFERENCE, the parameter must have the same or fewer bytes than the argument (the maximum length for an OCCURS DEPENDING ON group); a group cannot be passed BY VALUE; strongly typed groups and restricted pointers must match their type on both sides.

  • For BY VALUE, the parameter must be one of the simple item kinds listed above; when it is numeric, the check works as if a COMPUTE from the argument to the parameter were done, for a pointer as if a SET were done, otherwise as if a MOVE were done.

  • A prototype and its definition must agree with each other, and prototypes of the same name must agree with each other.

A violation is a compile-time error. That is the difference between a function and a subprogram that is called with CALL: passing a PIC 9(2) item where PIC 9(3) is declared is rejected for a function in Enterprise COBOL, and accepted silently for a subprogram, where the called program then reads a byte that belongs to something else. The COBOL standard has the same idea for programs (a program prototype, PROGRAM-ID. name IS PROTOTYPE, and a REPOSITORY entry with the word PROGRAM), but the Enterprise COBOL 6.5 Language Reference and Programming Guide describe prototypes for functions only, so CALL is not type-checked there.

GnuCOBOL 3.2 does not check the arguments of functions. A prototype was written for OVERDUE-FINE with a PIC 9(3) parameter, and a program was compiled with cobc -fsyntax-only -Wall that invokes it with a PIC 9(2) item, a PIC X(3) item, two arguments, a numeric literal and no arguments. The only diagnostic was the unfinished warning shown above: each of the five wrong invocations was accepted. It does check CALL, but only against a program prototype: with PROGRAM-ID. FINESUB IS PROTOTYPE. (two parameters, PIC 9(3) and PIC 9(3)V99) and PROGRAM FINESUB. in REPOSITORY, a CALL FINESUB USING WS-SMALL WS-TEXT with a PIC 9(2) and a PIC X(3) item, and a CALL FINESUB USING WS-SMALL with one argument, produced these warnings besides handling of PROGRAM PROTOTYPE is unfinished:

pp.cob:21: warning: argument #1, WS-SMALL, does not conform to the parameter definition [-Wrepository-checks]
pp.cob:21: warning: argument #2, WS-TEXT, does not conform to the parameter definition [-Wrepository-checks]
pp.cob:22: warning: argument #1, WS-SMALL, does not conform to the parameter definition [-Wrepository-checks]
pp.cob:22: warning: argument #2 is not optional [-Wrepository-checks]

So GnuCOBOL 3.2 has the check that Enterprise COBOL lacks (for CALL), and lacks the check that Enterprise COBOL has (for functions); both are warnings, not errors. A portable program cannot rely on either, and should keep the prototypes in one copybook so that a compiler that checks gets the same text everywhere.

One more difference shows when a program, rather than a function, tries to return a value. Enterprise COBOL allows PROCEDURE DIVISION USING ... RETURNING item on a called subprogram (but not on a main program, a nested program, or a program with ENTRY), and CALL name USING ... RETURNING item receives it. GnuCOBOL 3.2 reports program RETURNING is not implemented [-Wpending] when it compiles such a program; the program built from it failed at run time with attempt to reference invalid memory address, and the caller’s CALL ... RETURNING was reported with unexpected RETURNING item [-Wrepository-checks]. In portable code the subprogram returns its result through an extra BY REFERENCE parameter; a function needs no such work-around, which is a reason to prefer a function for a pure calculation.

Function or subprogram?

Both are reusable units that the rest of the system calls. The choice follows what the unit does and how it is called, not a fashion.

Question User-defined function Called subprogram (CALL)

How is it used?

As an operand in an expression: ADD OVERDUE-FINE(WS-DAYS) TO WS-TOTAL.

As a statement: CALL "name" USING ...; the result goes into a field and is used by the next statement.

Result

Exactly one, mandatory, through RETURNING; supported in both compilers.

Zero or many, through BY REFERENCE parameters; RETURNING works on Enterprise COBOL but is not implemented in GnuCOBOL 3.2.

Passing modes

BY REFERENCE, BY VALUE; BY CONTENT through CONTENT-OF (Enterprise COBOL).

BY REFERENCE, BY CONTENT, BY VALUE, OMITTED, OPTIONAL.

Type checking

At compile time against the definition or the prototype in Enterprise COBOL; none in GnuCOBOL 3.2.

None at compile time in Enterprise COBOL; the byte counts must agree, and the mistake shows up at run time. GnuCOBOL 3.2 warns only when a program prototype is declared.

Linkage

Fixed by the definition or the prototype (ENTRY-INTERFACE). Always recursive.

Static or dynamic, chosen by CALL literal or CALL identifier and the DYNAM option; CANCEL; INITIAL and RECURSIVE programs.

Files, SQL, CICS

SQL, CICS and JAVAIOP are not usable in a user-defined function in Enterprise COBOL.

Fully usable; a subprogram is how a shared Db2 or CICS routine is built.

State between calls

Use LOCAL-STORAGE (fresh each time); a function is always recursive.

WORKING-STORAGE keeps its values between calls unless the program is INITIAL or is `CANCEL`led.

Portability

Syntax portable to GnuCOBOL and Enterprise COBOL; module naming and checking differ.

The most portable form in COBOL; works in every compiler of the last forty years.

A rule of thumb for the Circulation code: a pure calculation on a few values that returns one number or string, such as the fine, the loan due date or a check digit of the ISBN, is a good function; a routine that reads or updates files, talks to Db2 or CICS, returns several values, or must run on a compiler that has no user-defined functions is a subprogram. The version-neutral form of the fine, written as a subprogram with the result in a second parameter, is shown next. It compiles with cobc -x -Wall finesub.cob (and with -std=ibm), no warnings, and prints the line below:

       IDENTIFICATION DIVISION.
       PROGRAM-ID. FINECALL.
       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01  WS-DAYS             PIC 9(3) VALUE 10.
       01  WS-FINE             PIC 9(3)V99.
       01  WS-SHOW             PIC ZZ9.99.
       PROCEDURE DIVISION.
           CALL "OVERDUE-FINE-SUB" USING WS-DAYS WS-FINE
           MOVE WS-FINE TO WS-SHOW
           DISPLAY WS-DAYS " days: " WS-SHOW
           STOP RUN.
       END PROGRAM FINECALL.

       IDENTIFICATION DIVISION.
       PROGRAM-ID. OVERDUE-FINE-SUB.
       DATA DIVISION.
       LINKAGE SECTION.
       01  L-DAYS              PIC 9(3).
       01  L-FINE              PIC 9(3)V99.
       PROCEDURE DIVISION USING L-DAYS L-FINE.
           COMPUTE L-FINE = L-DAYS * 0.35
           IF L-FINE > 5.00
               MOVE 5.00 TO L-FINE
           END-IF
           GOBACK.
       END PROGRAM OVERDUE-FINE-SUB.
010 days:   3.50

It needs a CALL, a field for the result and a separate statement to use it, where the function version is one expression; and the compiler checks nothing about the two parameters. On the other hand, it needs no REPOSITORY, no prototype and no module naming, and it can be changed to use Db2 later without moving to another kind of unit.

Differences between the compilers

Feature Enterprise COBOL 6.5 GnuCOBOL 3.2

FUNCTION-ID, END FUNCTION, RETURNING

Since 6.4. RETURNING required; the function is always recursive.

Supported; RETURNING required (error: RETURNING clause is required for a FUNCTION).

REPOSITORY. FUNCTION name.

Supported; only in the outermost program.

Supported; -Wprototypes warns when no definition or prototype was seen.

IS PROTOTYPE (function)

Supported since the 6.4 PTF; conformance checking; AS, ENTRY-NAME, ENTRY-INTERFACE.

Accepted with the warning handling of FUNCTION PROTOTYPE is unfinished; no checking.

Program prototype

Not described in the 6.5 manuals.

Accepted with the warning handling of PROGRAM PROTOTYPE is unfinished; CALL arguments are checked against it (-Wrepository-checks warnings).

BY VALUE for parameters

Supported with the restrictions above.

Not tested here.

CONTENT-OF

IBM intrinsic: a copy of a data item, for BY CONTENT arguments.

A different intrinsic function (takes a pointer); the IBM use is rejected.

Literal arguments

Checked against the parameter.

Passed at the literal’s own size; a mismatch gives wrong data without a diagnostic.

External name

AS 'literal', PGMNAME option, binder or DLL rules.

The function name with hyphens turned to double underscores is the module name that the run time loads, such as OVERDUE__FINE.so.

SQL, CICS, JAVAIOP in a function

Not usable.

Not applicable (cobc has no CICS or Java interoperability statements).

Common mistakes

  • Writing the function after the program that uses it, with no prototype. The compiler has not seen the function at the point of the invocation; cobc warns with -Wprototypes, and Enterprise COBOL needs the definition first, or a prototype, to check and to generate the call.

  • Leaving out RETURNING. A function without a result is a compile-time error. A routine with no result is a subprogram.

  • Passing a literal to a function in GnuCOBOL. A literal of another size than the parameter produces wrong data silently; move it to an item described like the parameter.

  • Expecting BY CONTENT. A function cannot declare its parameters BY CONTENT; with BY REFERENCE the function can change the caller’s field. Copy the argument with CONTENT-OF on z/OS, or move it to a work field in portable code.

  • Calling a function with CALL. Enterprise COBOL says the result is unpredictable. A function is invoked only as a function reference.

  • Using SQL, CICS or Java interoperability inside a function on z/OS. They are not available there. Use a subprogram for the data access and call it from the function’s caller.

  • Putting WORKING-STORAGE state in a function and expecting it to survive. A function is recursive and gets fresh LOCAL-STORAGE on each invocation; a function that must remember something between invocations needs EXTERNAL data or a file.

  • A prototype that differs from its definition. The point of the prototype is that the compiler compares them; keep one copybook and COPY it into the function’s source and the callers.

  • Linking the function before the program in one object. The function’s entry point becomes the entry point of the load module on z/OS. Supply the binder ENTRY statement (ENTRY CELQSTRT for LP(64)), or compile the function separately.

  • The module name in GnuCOBOL. A separately built function is found as OVERDUE__FINE (hyphens become two underscores), not as OVERDUE-FINE.