100 Languages Speedrun: Episode 31: Fortran
Tags: fortran
Published: 2021-12-21 16:24:11

The first programming language was Fortran (FORmula TRANslator). Since then, a lot of languages called "Fortran <year>" were created, each bringing it closer to modern programming and further away from its sources.

It would be difficult to actually run extremely old Fortran code, as that worked with punch cards, and data tapes. Even by the time of Fortran 77 a lot of the ancient Fortran features were either gone or marked as obsolete. But let's do our best - using GNU Fortran in legacy mode, and trying to make the code as old style as I can get away with.

Whenever I mention Fortran in this post, I mean the really old stuff. So don't tell me about Fortran 2018 and how it can do object oriented concurrent crypto mining while running in WASM on a tablet or whatever kids are into these days.

### Hello, World!

Obviously we'll be writing the program in full caps, as lower case letters weren't invented until 1970s. Well, at least computers didn't usually have them, to save precious memory.

Unlike all modern, Fortran has fixed column layout. Here's one attempt at Hello, World:

```
C      PRINTS "HELLO, WORLD!"
       PROGRAM HELLO
 1     PRINT *, 'HELLO, WORLD!'                                         NICE
       END
```

```
$ gfortran -std=legacy -o hello hello.f
$ ./hello
 HELLO, WORLD!
```

What the hell is going on here?

* column 1 is comment indicator - if there's anything non-blank there, the whole line is a comment. So you can have `//` or `#` or `;` or `--` comments in Fortran, whichever style you prefer! How convenient, right?
* columns 2-6 are for optional line number, we'll be doing goto a lot
* column 7 is line continuation indicator - if there's anything else than space (or `0` for some reason) it continues previous line
* columns 8-72 are for the actual code
* columns 73+ are ignored, so they're essentially also comments

Weird? We're just getting started.

### Hollerith strings

Fortran 77 introduced strings delimited by quotation marks. How did Fortran programmers do strings before then? You're in for a treat!

The old syntax for strings was character count, followed by `H`, followed by the string. If you miscounted, and people did all the time, that was a syntax error.

I assure you, I'm not trolling, this was actually the thing.

```
C      PRINTS "HELLO, WORLD!"
       PROGRAM HELLO
 1     PRINT *, 13HHELLO, WORLD!                                        EVEN NICER
       END
```

### Loop

Fortran 77 introduced some sort of structured programming. In the olden days, everything was based on line numbers. Take a look at this loop:

```
       PROGRAM LOOP
       PRINT *, 10HLOOP START
       DO 20 I = 10, 20, 2
 10    PRINT *, 2HI=
 20    PRINT *, I
 30    PRINT *, 9HLOOP DONE
       END
```

Which prints:

```
./loop
 LOOP START
 I=
          10
 I=
          12
 I=
          14
 I=
          16
 I=
          18
 I=
          20
 LOOP DONE
```

The `DO 20 I = 10, 20, 2` means `for i = 10 to 20 by 2` loop. The loop goes until line number listed in the `DO` statement, that is `20`. After the loop is over it goes to next statement after statement numbered `20`.

As for declaring variables, any variable starting with I, J, K, L, M, or N as integer. Any other name is a float. Oh and variable names could have at most 6 characters originally.

### FizzBuzz

Now that we know how to loop and print, it's fairly straightforward to do a FizzBuzz:

```
       PROGRAM FIZZBUZZ
       DO 40 I = 1, 20
 10    IF (MOD(I,15).NE.0) GOTO 20
       PRINT *, 8HFIZZBUZZ
       CONTINUE
 20    IF (MOD(I,5).NE.0) GOTO 30
       PRINT *, 4HBUZZ
       CONTINUE
 30    IF (MOD(I,3).NE.0) GOTO 40
       PRINT *, 4HFIZZ
       CONTINUE
 40    PRINT *, I
       END
```

That prints creatively formatted FizzBuzz:

```
$ ./fizzbuzz
           1
           2
 FIZZ
           3
           4
 BUZZ
           5
 FIZZ
           6
           7
           8
 FIZZ
           9
 BUZZ
          10
          11
 FIZZ
          12
          13
          14
 FIZZBUZZ
 BUZZ
 FIZZ
          15
          16
          17
 FIZZ
          18
          19
 BUZZ
          20
```

`MOD(I,15)` is `i % 15`. `.NE.` is `!=`. `CONTINUE` goes to the next iteration of the loop.

This formatting is quite bad, so how could we improve it?

### Double function

Let's write a very simple function, that doubles the integer it gets:

```
       PROGRAM DOUBLING
 10    FORMAT(7HDOUBLE(, I3, 2H)=, I4)
       DO 20 I = 1, 20
       J = DOUBLE(I)
 20    PRINT 10, I, J
       END

       FUNCTION DOUBLE(I)
       DOUBLE=I*2
       END
```

And run it:

```
./double
DOUBLE(  1)=   2
DOUBLE(  2)=   4
DOUBLE(  3)=   6
DOUBLE(  4)=   8
DOUBLE(  5)=  10
DOUBLE(  6)=  12
DOUBLE(  7)=  14
DOUBLE(  8)=  16
DOUBLE(  9)=  18
DOUBLE( 10)=  20
DOUBLE( 11)=  22
DOUBLE( 12)=  24
DOUBLE( 13)=  26
DOUBLE( 14)=  28
DOUBLE( 15)=  30
DOUBLE( 16)=  32
DOUBLE( 17)=  34
DOUBLE( 18)=  36
DOUBLE( 19)=  38
DOUBLE( 20)=  40
```

What's going on:

* we finally get nicely formatted output, that we can use `FORMAT` statement to setup some format (in this case equivalent of `"double(%3d)=%4d\n"`) - each `FORMAT` statement has a label (in this case `10`), and it's used to refer to format number - these are not just for `GOTO`
* `PRINT 10, I, J` uses the `10 FORMAT` we setup before
* `FUNCTION DOUBLE(I) ... END` defines a function
* assigning to function name `DOUBLE = I*2` sets which value will be returned, there's no direct equivalent of `RETURN I*2`

### Fibonacci

Now that we know how to do a function, let's do a Fibonacci sequence. Oh wait, even Fortran 77 did not support recursive functions at all.

For now let's use some "modern" Fortran extensions to have recursion.

```
       PROGRAM FIBONACCI
 10    FORMAT(4HFIB(, I3, 2H)=, I10)
       DO 20 I = 1, 20
       J = FIB(I)
 20    PRINT 10, I, J
       END

       RECURSIVE FUNCTION FIB(I) RESULT(A)
       IF (I-2) 30,30,40
 30    A=1
       RETURN
 40    A=FIB(I-1)+FIB(I-2)
       END
```

It prints what we'd expect:

```
FIB(  1)=         1
FIB(  2)=         1
FIB(  3)=         2
FIB(  4)=         3
FIB(  5)=         5
FIB(  6)=         8
FIB(  7)=        13
FIB(  8)=        21
FIB(  9)=        34
FIB( 10)=        55
FIB( 11)=        89
FIB( 12)=       144
FIB( 13)=       233
FIB( 14)=       377
FIB( 15)=       610
FIB( 16)=       987
FIB( 17)=      1597
FIB( 18)=      2584
FIB( 19)=      4181
FIB( 20)=      6765
```

There are a few things going on here:

* `RECURSIVE FUNCTION FIB(I) RESULT(A)` - declares recursive function, we also need to declare return variable, as otherwise `FIB=FIB(I-1)+FIB(I-2)` would be too confusing
* `RETURN` - we can return early
* `IF (I-2) 30,30,40` - the "arithmetic if", you pass a number, and three goto labels depending if the number is negative, zero, or positive - that's how Fortran programmers used to do `if (i <= 2)` equivalent back in the days

### Fibonacci without recursion

But back in the olden days, recursion was not allowed. Fortunately we already know the loop-based algorithm:

```
       PROGRAM FIBONACCI
 10    FORMAT(4HFIB(, I3, 2H)=, I10)
       DO 20 I = 1, 20
       J = FIB(I)
 20    PRINT 10, I, J
       END

       FUNCTION FIB(I)
       K1 = 0
       K2 = 1
       DO 30 J = 1, I
       K3 = K1 + K2
       K1 = K2
 30    K2 = K3
       FIB=K1
       END
```

It outputs the same thing. Any variable starting with `I`, `J`, or `K` is also an integer, so we didn't need to declare anything.

### Should you use Fortran?

As long as we're talking about these ancient versions, obviously not.

Some old languages like Forth or PostScript could enjoy a second life as an esoteric language, but I don't think old Fortran even has much potential there.

Supposedly modern Fortran generates slightly faster numerical code than C/C++ in some cases, so some numerical calculation libraries are still coded in Fortran (from what I remember, Fortran arrays can be assumed to not overlap, while C/C++ pointers can point anywhere, so Fortran compiler can do a few more optimizations), even if they are generally used from other languages. This is a somewhat marginal use, and you can probably get matching performance with some compiler hints, but in any case, this episode is only about old Fortran anyway.

### Code

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

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