100 Languages Speedrun: Episode 50: COBOL
Tags: cobol
Published: 2022-01-08 14:37:57

COBOL - Common Business Oriented Language - is one of the oldest programming languages. It was universally considered to be shit for pretty much its entire existence, because it was shit.

It's essentially dead. There are frequent claims in the media of "um, actually Cobol is super popular", but they're all bullshit journalists love to repeat after each other without any evidence - which is pretty much in line with the quality of tech journalism. You can easily see from stack overflow, any jobs website, or such, that the only thing people still Cobol for is keeping zombie system from passing away completely.

Just like Fortran, the language changed many times while keeping the name. I'll try to stick to classic Cobol from the punched card era, with its fixed column layout, even though many of less archaic dialects of Cobol allow a bit more flexibility.

### Hello, World!

```
      *    HELLO WORLD IN COBOL
       IDENTIFICATION DIVISION.
       PROGRAM-ID. HELLO.

       PROCEDURE DIVISION.
           DISPLAY 'HELLO WORLD'.
           STOP RUN.
```

```
$ cobc -xg hello.cob
$ ./hello
HELLO WORLD
```

That's some 2D picture.

Let's take it line by line:

* `IDENTIFICATION DIVISION.` - some metadata about the procedure
* `PROGRAM-ID. Hello.` - the name of the procedure
* `DATA DIVISION.` - there's also one there usually, defines data
* `PROCEDURE DIVISION.` - code of the procedure
* `DISPLAY 'HELLO WORLD.` - displays a string and newline
* `STOP RUN.` - exits the program

Everything in caps to keep with the spirit of Cobol.

And column by column:

* first 6 columns are for statement numbers
* column 7 is comment indicator - there are a few possible characters for it, we'll be using `*`
* names, divisions etc. should start from column 8
* everything else should start from column 12

### Loop

Let's create a loop that prints a number from 1 to 20:

```
       IDENTIFICATION DIVISION.
       PROGRAM-ID. LOOP.

       DATA DIVISION.
       WORKING-STORAGE SECTION.
           01 N PIC 9(2).

       PROCEDURE DIVISION.
           PERFORM VARYING N FROM 1 BY 1 UNTIL N > 20
             DISPLAY N
           END-PERFORM.
           STOP RUN.
```

It works:

```
$ cobc -xg loop.cob
$ ./loop
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
```

Line by line:

* inside `DATA DIVISION.`, there's `WORKING-STORAGE SECTION.` which defines all local variables
* `01 N PIC 9(2).` defines `N` as two digit decimal number
* that `01` is about nested data definitions (so a 6 character date variable can contain inside it three 2 character variables for year, month, and day etc.), `01` means regular variable
* `PERFORM VARYING N FROM 1 BY 1 UNTIL N > 20` to `END-PERFORM` loops `N` from `1` to `20`
* `DISPLAY N` prints `N`, with leading zeroes

The Y2K panic was largely about such `PIC 9(2)`s used by old COBOL code from the 1960s and 1970s to define year fields, and similar poor practices in other systems. People pretty much forgot about Y2K panic by now, but it was a huge cultural phenomenon back then. Like most such panics, it was over pretty much nothing. After their predictions of doom all turned false, people tried to rewrite history and claim there weren't many bugs thanks to some heroic effort to preemptively fix them ([Wikipedia still contains such lies](https://en.wikipedia.org/wiki/Year_2000_problem)). In reality, there wasn't much there to begin with, and it was just panic fueled by the media, and Big Tech of the day trying to sell Y2K preparedness services.

### FIZZBUZZ

Let's write the FIZZBUZZ! As we're doing COBOL, it will be all caps and with leading zeroes.

```
       IDENTIFICATION DIVISION.
       PROGRAM-ID. FIZZBUZZ.

       DATA DIVISION.
       WORKING-STORAGE SECTION.
           01 N PIC 9(3).
           01 M PIC 9(3).
           01 3REM PIC 9(1).
           01 5REM PIC 9(1).

       PROCEDURE DIVISION.
           PERFORM VARYING N FROM 1 BY 1 UNTIL N > 100
             DIVIDE N BY 3 GIVING M REMAINDER 3REM
             DIVIDE N BY 5 GIVING M REMAINDER 5REM
             EVALUATE 3REM ALSO 5REM
               WHEN ZERO ALSO ZERO
                 DISPLAY 'FIZZBUZZ'
               WHEN ANY ALSO ZERO
                 DISPLAY 'BUZZ'
               WHEN ZERO ALSO ANY
                 DISPLAY 'FIZZ'
               WHEN OTHER
                 DISPLAY N
             END-EVALUATE
           END-PERFORM.
           STOP RUN.
```

```
$ cobc -xg fizzbuzz.cob
$ ./fizzbuzz
001
002
FIZZ
004
BUZZ
FIZZ
007
008
FIZZ
BUZZ
011
FIZZ
013
014
FIZZBUZZ
016
017
FIZZ
019
BUZZ
...
```

What's going on:

* all variables defined to fit exact number of digits they need
* variable names can start with numbers, something that's not really a thing in any language these days
* `EVALUATE 3REM ALSO 5REM` is a case statement over two variables - interestingly a lot of languages don't really allow that. Apparently it was only added in COBOL 85 and not available earlier.
* `WHEN ZERO ALSO ANY` etc. - are various matches, including wildcard matches

### Fibonacci

This was surprisingly difficult to get working. I think a lot of functions like `COMPUTE` use newfangled COBOL features from the 1970s+. When I tried to do more "classic" COBOL, GnuCOBOL was not too happy about it. Then again, the original COBOL didn't even have recursion, so it's hard to pick up the right year to target.

Interesting COBOL feature is `M PIC Z(8)9` - we're defining `M` as having 8 digits which should not be printed if zero (`Z(8)`), and one regular digit (`9`). Data storage and formatting are intertwined like this.

```
      *    MAIN PROGRAM
       IDENTIFICATION DIVISION.
       PROGRAM-ID. FIBLOOP.

       ENVIRONMENT DIVISION.
       CONFIGURATION SECTION.
       REPOSITORY.
           FUNCTION FIB.

       DATA DIVISION.
       WORKING-STORAGE SECTION.
           01 N PIC 9(3).
           01 M PIC Z(8)9.

       PROCEDURE DIVISION.
           PERFORM VARYING N FROM 1 BY 1 UNTIL N > 20
             COMPUTE M = FIB(N)
             DISPLAY 'FIB(' WITH NO ADVANCING
             DISPLAY N WITH NO ADVANCING
             DISPLAY ')=' WITH NO ADVANCING
             DISPLAY M
           END-PERFORM.
           STOP RUN.

       END PROGRAM FIBLOOP.

      *    FUNCTION FIB(N)
       IDENTIFICATION DIVISION.
       FUNCTION-ID. FIB.

       DATA DIVISION.
       LOCAL-STORAGE SECTION.
       01 A PIC 9(9).
       01 B PIC 9(9).
       01 N1 PIC 9(3).
       01 N2 PIC 9(3).

       LINKAGE SECTION.
       01 N PIC 9(3).
       01 RESULT PIC 9(9) COMP BASED.

       PROCEDURE DIVISION USING N RETURNING RESULT.
           IF N IS LESS OR EQUAL TO 2 THEN
               MOVE 1 TO RESULT
           ELSE
              SUBTRACT 1 FROM N GIVING N1
              SUBTRACT 2 FROM N GIVING N2
              COMPUTE A = FIB(N1)
              COMPUTE B = FIB(N2)
              ADD A TO B GIVING RESULT
           END-IF.
           GOBACK.

       END FUNCTION FIB.
```

```
$ cobc -xg fib.cob
$ ./fib
FIB(001)=        1
FIB(002)=        1
FIB(003)=        2
FIB(004)=        3
FIB(005)=        5
FIB(006)=        8
FIB(007)=       13
FIB(008)=       21
FIB(009)=       34
FIB(010)=       55
FIB(011)=       89
FIB(012)=      144
FIB(013)=      233
FIB(014)=      377
FIB(015)=      610
FIB(016)=      987
FIB(017)=     1597
FIB(018)=     2584
FIB(019)=     4181
FIB(020)=     6765
```

There's a lot of weird stuff going on here. There's `ENVIRONMENT DIVISION` listing which functions we're using. There's `LINKAGE SECTION` for function's inputs and outputs.

Basically 90% of this trivial program is ridiculous boilerplate which you need to get just right. I wanted to check a bunch of examples of COBOL code, and every example used different boilerplate, and none would work on GnuCOBOL without serious tweaking - even ones tagged as being specifically for GnuCOBOL. The whole language is far more of a dumpster fire than I expected. Fortran was not amazing, but at least you could see imagine actual humans coding Fortran. I have no idea what kind of subterranean reptilian creatures from the Hollow Earth COBOL was designed for, because it sure as hell wasn't designed for any kind of human beings.

### Should you use COBOL?

People knew COBOL was shit before Joe Biden was even born, so obviously no.

It might very well be the absolute worst language reviewed so far. Even Befunge and Thue were more enjoyable to code in.

### Code

[All code examples for the series will be in this repository](https://github.com/taw/100-languages-speedrun).

[Code for the COBOL episode is available here](https://github.com/taw/100-languages-speedrun/tree/master/episode-50-cobol).