Кракозя́бры
Adventures in Fortran

Casual observations on ancient programming follow.

Numerical, scientific programming is often done in Fortran. Here is a sample of “common FORTRAN 77 and FORTRAN IV” (it’s not actually conformant to either) from a book I have on modeling neural systems. I typed it up from the book in emacs, which means it’s not exactly the same: specifically, I went with whatever fortran-mode said the spacing should be rather than the book, I elided the “University of Colorado Computing System”-specific directives at the beginning and end, and the font is slightly less ugly even with <pre> rendering.

I’d like to emphasize that modern Fortran doesn’t look like this. The newest standard is Fortran 2008 and has lots of things that have come into vogue since the 70s, such as object-orientation and function pointers. And recursive functions.

      PROGRAM PTNRN10 (INPUT,OUTPUT,TAPE6=OUTPUT)
*                                                                                                                              
****  THIS PROGRAM SIMULATES THE RESPONSES OF A THREE-STATE-                                                                   
*     VARIABLE MODEL OF A SINGLE POINT NEURON TO APPLIED STEP                                                                  
*     AND RAMP INPUT CURRENTS.                                                                                                 
****                                                                                                                           
      INTEGER SCSTRT,SCSTP
      PARAMETER STEP=1.,EK=-10.
      DIMENSION P(500)
*                                                                                                                              
****  THIS SECTION READS AND WRITES THE INPUT PARAMETERS                                                                       
*                                                                                                                              
      READ 5010 C,TTH,B,TGK,TH0,TMEM
      READ 5020 SC,SL,SCSTRT,SCSTP
      READ 5030 LTSTOP
*                                                                                                                              
      WRITE (6,6000)
      WRITE (6,6010) C,TTH,B,TGK,TH0,TMEM
      WRITE (6,6020) SC,SL,SCSTRT,SCSTP
      WRITE (6,6030) LTSTOP
*                                                                                                                              
****  THIS SECTION INITIALIZES VARIABLES                                                                                       
*                                                                                                                              
      E=0. $TH=TH0 $S=0. $GK=0.
      DCTH=EXP(-STEP/TTH) $DGK=EXP(-STEP/TGK)
*                                                                                                                              
****  THIS SECTION UPDATES TIME VARIABLES AT EACH TIE STEP                                                                     
*                                                                                                                              
      DO 1000 L=1,LTSTOP
*                                                                                                                              
         SCN=0. $IF(L.GE.SCSTRT.AND.L.LT.SCSTP) SCN=SC+SL*FLOAT(L-SCSTRT)
*                                                                                                                              
         GK=GK*DGK+B*S*(1.-DGK) $GTOT=1.+GK $DCE=EXP(-GTOT*STEP/TMEM)
         E=E*DCE+(SCN+GK*EK)*(1.-DCE)/GTOT
         TH=TH0+(TH-TH0)*DCTH+C*E*(1.-DCTH)
         S=0. $IF(E.GE.TH) S=1. $P(L)=E+S*(50.-E)

 1000 CONTINUE
*                                                                                                                              
****  THIS SECTION WRITES OUT ACTIVITY VARIABLES                                                                               
*                                                                                                                              
      WRITE (6,6050) (P(L),L=1,LTSTOP)
*                                                                                                                              
****  THESE ARE THE FORMATS                                                                                                    
*                                                                                                                              
 5010 FORMAT(12F6.2)
 5020 FORMAT(2F6.2,2I6)
 5030 FORMAT(I6)
 6000 FORMAT(1H1)
 6010 FORMAT(5X,*C,TTH,B,TGK,TH0,TMEM=*,6F7.2)
 6020 FORMAT(5X,*SC,SL,SCSTRT,SCSTP=*,2F7.2,2I7)
 6030 FORMAT(5X,*LSTSTOP=*,I7)
 6050 FORMAT(/,(2X,20F6.2))
      STOP
      END

Rundown of syntax: Lines beginning with asterisks are comments. Lines beginning with numbers have those numbers as labels (you know? like with gotos?). Dollar signs indicate (to Colorado U’s idiosyncratic system - I don’t know why the author thought publishing nonconformant code was okay, maybe nobody actually implemented the standard) to start a new line, that is, a = 1 $b = 2 is like a = 1; b = 2 in a more C-ish language. INTEGER is a directive that the named variables are integers; PARAMETER is the same for compile-time parameters; DIMENSION P(500) says that P is an array 500 long. “READ # variables” means to read the values of the variables from input using the format at label #. I think this does indeed mean that the types of those variables are to be declared from the format string. Write (whatever,#) variables uses the format at label # to write the values of the given variables to whatever, an output indicator - in this case, 6, which I gather from the PROGRAM statement is supposed to be a tape. DO… well, it’s like for(l=1;l<ltstop;++l) up to the continue. Probably. I’m not even going to try to explain those format strings. And STOP and END should be clear enough.

This will not compile under either GCC’s Fortran compiler or whatever the other implementation I found in Debian is. It is nonconformant for several reasons: the asterisks in the format strings are apparently disallowed, PROGRAM is not allowed to have arguments (the parenthesized variables), and the dollar sign thing is totally idiosyncratic. This is acknowledged in the book, and the author seems to think that anybody who knew their Fortran system (I don’t know my Fortran system) would be able to fix it up for that system with only minor alterations.

Why am I explaining this? Because I want to see if explaining it will help my own understanding, of course, but also because I translated the code into modern Lisp and had an odd thought about it. Here is that translation (I haven’t tested it yet):

(defparameter *STEP* 1)
(defparameter *EK* -10)

(defun ptnrn10 (c tth b tgk th0 tmem sc sl scstrt scstp ltstop)
  (declare (type integer scstrt scstp))
  (let ((p (make-array 500)))
    (let ((e 0)
          (th th0)
          (s 0)
          (gk 0)
          (dcth (exp (/ (- *step*) tth)))
          (dgk (exp (/ (- *step*) tgk))))
      (dotimes (l ltstop p)
        (let ((scn (if (and (>= l scstrt) (< l scstp))
                       (+ sc sl (float (- l scstrt)))
                       0)))
          (setf gk (+ (* gk dgk) (* b s (1- dgk))))
          (let* ((gtot (1+ gk))
                 (dce (exp (* (- gtot) *step* (/ tmem)))))
            (setf e (+ (* e dce) (* scn gk *ek*) (1- dce) (/ gtot))
                  th (+ th0 (* (- th th0) dcth) (* c e (1- dcth)))
                  s (if (>= e th) 1 0)
                  (svref p l) (+ e (* s (- 50 e))))))))))

Obviously it’s not a very faithful translation. I changed the program to a function, moved the compile-time parameters outside the function, removed the comments (which were mostly just restating the code anyway), changed the reads and writes to the Lispier (and, by now, everything-else-ier, I hope) parameters and return value, used lexical blocks when possible, and left out types.

But besides that it’s basically the same algorithm. It’s still iterative, does mostly arithmetic, all that stuff. And I still have no idea what it does, beyond that it simulates a neuron. The variable names are impenetrably derived from biophysics equations. The operation of the algorithm is unclear; it’s not immediately obvious that it’s approximating a differential equation, and even then it’s hard to tell what’s part of that approximation and what’s part of the neuron. It has several unexplained literals, such as that 50.

It sucks. It’s near impossible to maintain due to the lack of structure or notes. Write-only code par excellence.

My observations here are that bad communication can cross linguistic barriers, and that non-programmers who write programs really need to get infused with senses of style and cleanliness in their programming classes.

Side note: If anyone reading this happens to actually know Fortran, I’d love to get some good tutorials and so on. Though I doubt any of them could help me with this.

  1. mnxmnkmnd posted this