Functions in COBOL
Learn how COBOL organizes reusable logic with PERFORM paragraphs, CALL subprograms, parameters via the LINKAGE SECTION, scope, and recursion - all with Docker-ready GnuCOBOL examples
Most languages have a single construct called a “function.” COBOL, true to its procedural, business-oriented design, offers two distinct ways to package reusable logic, and neither is called a function in the traditional sense.
Inside a single program, you organize code into paragraphs (and sections) and execute them with the PERFORM verb. To share logic across programs - the COBOL equivalent of a library call - you write a separate subprogram and invoke it with CALL, passing data through the LINKAGE SECTION. COBOL 2002 added intrinsic functions and recursion to the mix as well.
This tutorial walks through both mechanisms: PERFORM for in-program structure and CALL for true subprograms with parameters and return values. Because COBOL has no block-level scope, we’ll also see how data sharing actually works, and finish with a recursive subprogram that computes a factorial. Throughout, we’ll use modern free-format syntax with GnuCOBOL.
Paragraphs as Functions
The most common unit of reuse in COBOL is the paragraph: a named block of statements in the PROCEDURE DIVISION. You invoke it with PERFORM, which is COBOL’s “call this routine and come back” verb. PERFORM also has built-in looping forms - PERFORM n TIMES and PERFORM ... VARYING - that fold iteration into the call itself.
Create a file named functions.cob:
| |
PERFORM SHOW-BANNER runs that paragraph once and returns. PERFORM GREET 3 TIMES repeats the paragraph three times. PERFORM COUNT-LINE VARYING ... runs the paragraph in a loop, incrementing WS-I until the condition is met. Note that STOP RUN sits at the end of MAIN-PARAGRAPH so the program halts before “falling through” into the paragraphs below - those are only reached when explicitly performed.
Sharing Data and Scope
COBOL has no local variables. Every item declared in WORKING-STORAGE SECTION is global to the entire program, and every paragraph can read and write all of it. A paragraph “receives arguments” simply by reading shared fields and “returns a value” by writing to one. This is very different from languages with parameter lists and local scope - it makes COBOL paragraphs simple, but it also means you must be deliberate about which fields each paragraph touches.
Create a file named average.cob:
| |
SUM-VALUES writes its result into the shared WS-TOTAL; COMPUTE-AVERAGE then reads that same field. There are no parameters - the data flows through global WORKING-STORAGE. Notice the output: a PIC 9(4) field always displays its full width with leading zeros, so 90 prints as 0090. To suppress those zeros you would use an edited picture like PIC ZZZ9 (where Z blanks leading zeros).
Subprograms with CALL
For logic you want to reuse across multiple programs, COBOL uses a separate subprogram: its own compilation unit with its own IDENTIFICATION DIVISION. The caller uses CALL "NAME" USING ... to pass arguments, and the subprogram declares matching fields in its LINKAGE SECTION. Arguments are passed by reference by default, so the subprogram can modify the caller’s data - which is exactly how a subprogram “returns” a result. GOBACK returns control to the caller.
Create a file named square.cob:
| |
Create a file named mainsq.cob:
| |
The USING clause on the CALL and the USING clause on the subprogram’s PROCEDURE DIVISION line up by position: WS-VALUE maps to LK-INPUT, and WS-SQUARE maps to LK-RESULT. The two fields in each pair must have compatible pictures. Because LK-RESULT is passed by reference, writing to it inside SQUARE updates WS-SQUARE back in the caller.
Recursion
COBOL programs are not recursive by default - calling a program that is already active is an error. To allow it, mark the subprogram RECURSIVE in its PROGRAM-ID. Each recursive invocation also needs its own copy of any temporary data, so we place that field in the LOCAL-STORAGE SECTION, which is allocated fresh on every call (unlike WORKING-STORAGE, which is shared across invocations).
Create a file named recurse.cob:
| |
Both programs live in one source file, separated by END PROGRAM. The main program RECURSE calls FACT, which calls itself with LK-N - 1 until it reaches the base case (LK-N <= 1). Because LS-PREV lives in LOCAL-STORAGE, each level of the recursion keeps its own value while the call below it runs - placing it in WORKING-STORAGE instead would corrupt the computation, since every invocation would share the same memory.
Running with Docker
Compile and run each example with GnuCOBOL inside the official container. The -x flag builds an executable and -free enables free-format source.
| |
For the subprogram example, both mainsq.cob and square.cob are passed to cobc in one command; the first file becomes the entry point and produces the mainsq executable.
Expected Output
functions.cob:
Calling paragraphs like functions:
==============================
Paragraphs make code reusable.
Paragraphs make code reusable.
Paragraphs make code reusable.
Line number 01
Line number 02
Line number 03
Line number 04
Line number 05
==============================
average.cob:
Total: 0090
Average: 030
mainsq.cob:
Calling the SQUARE subprogram...
12 squared is 144
recurse.cob:
5! = 120
Key Concepts
PERFORMruns paragraphs - A paragraph is COBOL’s in-program routine;PERFORMcalls it and returns.PERFORM n TIMESandPERFORM ... VARYINGadd looping to the call.- No local scope - All
WORKING-STORAGEdata is global. Paragraphs “pass arguments” by reading shared fields and “return values” by writing them. CALLinvokes subprograms - A subprogram is a separate program unit.CALL "NAME" USING ...passes arguments to fields declared in the callee’sLINKAGE SECTION.- Arguments are passed by reference - The default
BY REFERENCEmeans a subprogram can modify the caller’s data, which is how results are returned. UseBY CONTENTto pass a copy instead. GOBACKvsSTOP RUN-GOBACKreturns from a subprogram to its caller;STOP RUNhalts the entire run unit. UseGOBACKin subprograms.- Recursion is opt-in - Mark a program
RECURSIVEto let it call itself, and put per-call temporaries inLOCAL-STORAGE SECTIONso each invocation gets its own copy. PICcontrols display width - NumericPIC 9fields show leading zeros (0090); edited pictures withZblank them for cleaner reports.COPYfor shared definitions - BeyondPERFORMandCALL, COBOL reuses data and code layouts across programs by pulling in copybooks with theCOPYstatement.
Running Today
All examples can be run using Docker:
docker pull esolang/cobol:latest
Comments
Loading comments...
Leave a Comment