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-IDinstead ofPROGRAM-IDand ends withEND FUNCTION name. -
It is never nested: a function cannot be written inside a program, another function, a method or a class.
-
Its
PROCEDURE DIVISIONheader must have aRETURNINGphrase, which names the data item that holds the result.USINGis optional and lists zero or more parameters. -
It is called by writing its name where an operand is allowed, not by the
CALLstatement. Enterprise COBOL says that a function reached throughCALLbehaves 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
CALLINTERFACEdirective does not affect it; its linkage is chosen byENTRY-INTERFACE(STATICby default,DYNAMICorDLL). -
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,SUMandWHEN-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 theREPOSITORYparagraph of theCONFIGURATION SECTION:FUNCTION OVERDUE-FINE.The name then cannot be used as an ordinary user-defined word in that scope. Several functions can follow oneFUNCTIONword or be written as separate entries;FUNCTION ALL INTRINSICandFUNCTION name INTRINSICdeclare intrinsic functions the same way. AREPOSITORYparagraph 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 withCALL: 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 CONTENTfor the formal parameters of a function. To make an argument arrive as a copy, declare the parameterBY REFERENCEand wrap the argument at the call in the intrinsic functionCONTENT-OF, as inOVERDUE-FINE(CONTENT-OF(WS-DAYS)).CONTENT-OFreturns a temporary copy of the same size, and the function modifies the copy. -
The result.
RETURNINGnames a level-01 or level-77LINKAGE SECTIONitem; 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:
-
RETURNINGis required; the division has no statements, declaratives or paragraphs. -
A prototype has no
REPOSITORYparagraph,WORKING-STORAGE,LOCAL-STORAGEorFILE SECTION; only theLINKAGE SECTIONitems 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 asGetRecordcan call a function that the operating environment knows asQQGETREC;ENTRY-NAME IS LONGMIXEDallows a mixed-case external name;ENTRY-INTERFACEchoosesSTATIC,DYNAMICorDLLlinkage for the generated call. -
The prototype is best kept in a copybook and brought in with
COPYby 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.
Source: Programming Guide: Using function prototypes; Programming Guide: Function prototype best practices; Language Reference: COBOL function prototype definition structure; Language Reference: Function prototype data; Programming Guide: Link-editing user-defined functions; APAR PH57397: COBOL user-defined function prototypes
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.
Source: GnuCOBOL 3.2 manual
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 inPICTURE,USAGE,SIGN,JUSTIFIED,BLANK WHEN ZEROandDYNAMIC 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 whenDECIMAL-POINT IS COMMAis 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 anOCCURS DEPENDING ONgroup); a group cannot be passedBY 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 aCOMPUTEfrom the argument to the parameter were done, for a pointer as if aSETwere done, otherwise as if aMOVEwere 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: |
As a statement: |
Result |
Exactly one, mandatory, through |
Zero or many, through |
Passing modes |
|
|
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 ( |
Static or dynamic, chosen by |
Files, SQL, CICS |
SQL, CICS and |
Fully usable; a subprogram is how a shared Db2 or CICS routine is built. |
State between calls |
Use |
|
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 |
|---|---|---|
|
Since 6.4. |
Supported; |
|
Supported; only in the outermost program. |
Supported; |
|
Supported since the 6.4 PTF; conformance checking; |
Accepted with the warning |
Program prototype |
Not described in the 6.5 manuals. |
Accepted with the warning |
|
Supported with the restrictions above. |
Not tested here. |
|
IBM intrinsic: a copy of a data item, for |
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 |
|
The function name with hyphens turned to double underscores is the module name that the run time loads, such as |
SQL, CICS, |
Not usable. |
Not applicable ( |
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;
cobcwarns 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 parametersBY CONTENT; withBY REFERENCEthe function can change the caller’s field. Copy the argument withCONTENT-OFon 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-STORAGEstate in a function and expecting it to survive. A function is recursive and gets freshLOCAL-STORAGEon each invocation; a function that must remember something between invocations needsEXTERNALdata 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
COPYit 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
ENTRYstatement (ENTRY CELQSTRTforLP(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 asOVERDUE-FINE.
References
-
Enterprise COBOL for z/OS 6.5 Language Reference: COBOL user-defined function definition structure
-
Enterprise COBOL for z/OS 6.5 Language Reference: COBOL function prototype definition structure
-
Enterprise COBOL for z/OS 6.5 Language Reference: Function prototype data
-
Enterprise COBOL for z/OS 6.5 Language Reference: FUNCTION-ID paragraph
-
Enterprise COBOL for z/OS 6.5 Language Reference: REPOSITORY paragraph
-
Enterprise COBOL for z/OS 6.5 Language Reference: Function-identifier
-
Enterprise COBOL for z/OS 6.5 Language Reference: The PROCEDURE DIVISION header
-
Enterprise COBOL for z/OS 6.5 Language Reference: CONTENT-OF
-
Enterprise COBOL for z/OS 6.5 Programming Guide: Using user-defined functions
-
Enterprise COBOL for z/OS 6.5 Programming Guide: Compiling user-defined function definitions
-
Enterprise COBOL for z/OS 6.5 Programming Guide: Link-editing user-defined functions
-
Enterprise COBOL for z/OS 6.5 Programming Guide: Using function prototypes
-
Enterprise COBOL for z/OS 6.5 Programming Guide: Function prototype best practices
-
Enterprise COBOL for z/OS 6.5 Programming Guide: Language standard considerations
-
Tom Taulli, Modern Mainframe Development, O’Reilly Media, 2022 (consulted for the topics only)