7. Program units

In addition to the four old program units: PROGRAM (that is the main program), SUBROUTINE, FUNCTION and BLOCK DATA, the new concept MODULE has been added, as well as some new things in the old units. Once again, subprogram is the common concept for both SUBROUTINE and FUNCTION.

Again I wish to emphasis that under Fortran 77 all program units are essentially on the same level, even if the main program logically is superior to the subroutines and functions that are called, and even though you could write a call map that looks like a tree. In reality the BLOCK DATA is on a higher level and all the other program units are on the same level, from the Fortran system viewpoint with the main program just a little above. The exception are the so-called statement functions with definitions that have to be first in a program unit, directly after the specification, and are internal to that unit and therefore on a logically lower level. Regrettably, the typical Fortran 77 programmer does not use statement functions.

The above means that all routine names are on the same logical level, which means that two different routines, and two different parts of a big program are not permitted to have the same name. Quite often numerical and graphical libraries include thousands of functions and subroutines, and each routine name consists of at most six characters under old Fortran standards. Therefore, there is a very great risk of a conflict of names. This problem could be partially solved by the old statement functions, since these are internal to the respective unit, and therefore different statement functions can have the same name if they are in different units. The disadvantage is that they can only treat what is in only one program line. But they can call each other in such a way that a later statement function can call an earlier statement function, but of course not the opposite.

F90 adds internal functions and internal subroutines, providing greater flexibility. They are specified at the end of each program unit (but not in the BLOCK DATA) after the new command CONTAINS and before the END. An internal subprogram can have access to the same variables as the unit it belongs to, including the possibility of calling the unit's other internal subprograms. It is written as an ordinary subprogram, but it is not permitted to have any internal functions or subroutines. The internal function is a modern replacement for the statement function.

The usual subroutines and functions remain the same as the earlier external subroutines and external functions, but there is now a more compelling reason for this name (that is calling them external) than earlier, since now you have also internal subprograms. Previously you only had the built in (intrinsic) functions as an alternative. In addition, the number of intrinsic functions has greatly increased, and a few intrinsic subroutines have been added.

For every argument in the specification of variables for subprograms we can now give its INTENT as IN, OUT or INOUT. If IN is valid, then the actual argument can be an expression like X+Y or SIN(X) or a constant like 37, since the value is only to be transferred to the subprogram, but a new value is not to be returned to the calling unit. The variables in this case may not be assigned a new value in the subprogram. If OUT is valid, on the other hand, the actual argument has to be a variable. At entry to the subprogram the variable is at this stage considered to be not defined. The third case covers both possibilities, one value on input and another on output, or possibly the same value. In this case the actual argument must also be a variable. If a variable has a pointer attribute then INTENT may not be given. The implementation of INTENT is not yet complete in all compilers.

One use for the new program unit MODULE is to take care of global data. As such it replaces the BLOCK DATA. Its other use is to make a package of new data types. A rather long example would be a package for interval arithmetic. Corresponding to each value X you have an interval (X_lower; X_upper). When using the package, you want to give only the variable name X when you mean the interval. The variable X is then supposed to be a new data type, interval. The following is in the file interval_arithmetics.f90 or intv_ari.f90.

MODULE INTERVAL_ARITHMETICS
       TYPE INTERVAL
              REAL LOWER, UPPER
       END TYPE INTERVAL
       INTERFACE OPERATOR (+)
              MODULE PROCEDURE INTERVAL_ADDITION
       END INTERFACE
       INTERFACE OPERATOR (-)
              MODULE PROCEDURE INTERVAL_SUBTRACTION
       END INTERFACE
       INTERFACE OPERATOR (*)
              MODULE PROCEDURE INTERVAL_MULTIPLICATION
       END INTERFACE
       INTERFACE OPERATOR (/)
              MODULE PROCEDURE INTERVAL_DIVISION
       END INTERFACE
CONTAINS
       FUNCTION INTERVAL_ADDITION(A, B)
              TYPE(INTERVAL), INTENT(IN) :: A, B
              TYPE(INTERVAL) :: INTERVAL_ADDITION
              INTERVAL_ADDITION%LOWER = A%LOWER + B%LOWER
              INTERVAL_ADDITION%UPPER = A%UPPER + B%UPPER
       END FUNCTION INTERVAL_ADDITION

       FUNCTION INTERVAL_SUBTRACTION(A, B)
              TYPE(INTERVAL), INTENT(IN) :: A, B
              TYPE (INTERVAL) :: INTERVAL_SUBTRACTION
              INTERVAL_SUBTRACTION%LOWER = A%LOWER - B%UPPER
              INTERVAL_SUBTRACTION%UPPER = A%UPPER - B%LOWER
       END FUNCTION INTERVAL_SUBTRACTION

       FUNCTION INTERVAL_MULTIPLICATION(A, B)
!             POSITIVE NUMBERS ASSUMED
              TYPE(INTERVAL), INTENT(IN) :: A, B
              TYPE (INTERVAL) :: INTERVAL_MULTIPLICATION
              INTERVAL_MULTIPLICATION%LOWER = A%LOWER * B%LOWER
             INTERVAL_MULTIPLICATION%UPPER = A%UPPER * B%UPPER
       END FUNCTION INTERVAL_MULTIPLICATION
       FUNCTION INTERVAL_DIVISION(A, B)
!             POSITIVE NUMBERS ASSUMED
              TYPE(INTERVAL), INTENT(IN) :: A, B
              TYPE(INTERVAL) :: INTERVAL_DIVISION
              INTERVAL_DIVISION%LOWER = A%LOWER / B%UPPER
              INTERVAL_DIVISION%UPPER = A%UPPER / B%LOWER
       END FUNCTION INTERVAL_DIVISION
END MODULE INTERVAL_ARITHMETICS
Compilation of the above results in the creation of the file interval_arithmetics.mod. This file includes an interesting modified version of the code above. Any program that makes use of this package, must contain the statement USE INTERVAL_ARITHMETICS at the beginning of the specification statements. This makes the data type INTERVAL and the four arithmetic calculations on this type directly available. In some cases it is desirable to only include some of the facilities in a module. In this case you use the ONLY attribute within the new USE statement.
       USE module_name, ONLY : list_of_chosen_routines
The following is an example of a very simple main program for the test of interval arithmetics. It is from the file interval.f90 or intv.f90.
    USE INTERVAL_ARITHMETICS
    IMPLICIT NONE
    TYPE (INTERVAL) :: A, B, C, D, E, F
    A%LOWER = 6.9            
    A%UPPER = 7.1
    B%LOWER = 10.9          
    B%UPPER = 11.1
    WRITE (*,*) A, B
    C = A + B                
    D = A - B
    E = A * B             
    F = A / B
    WRITE (*,*) C, D    
    WRITE (*,*) E, F
    END
Running this program on a Sun-computer with the NAG compiler results in the following output:
       % f90 interval_arithmetics.f90 interval.f90
       interval_arithmetics.f90:
       interval.f90:    
       % a.out
          6.9000001  7.0999999  10.8999996  11.1000004
         17.7999992 18.2000008  -4.2000003  -3.7999997
         75.2099991 78.8100052   0.6216216   0.6513762
       % exit
We compiled the program with the compiler f90, and the executable program was automatically named a.out. With the order above (the module first) the compilation also works with the SunSoft and Digital compilers!

In a module some concepts can be defined as PRIVATE, which means that the program units outside of this module are not able to use this concept. Sometimes an explicit PUBLIC declaration is used, normally PUBLIC is default. The following statements

       PRIVATE
       PUBLIC :: VAR1
result in all variables being local, except VAR1, which is global. Note that both these concepts (PUBLIC and PRIVATE) either can be given as a statement, for example
       INTEGER                        :: IVAR
       PRIVATE                        :: IVAR
or as an attribute
       INTEGER, PRIVATE               :: IVAR

Exercises

(7.1) Generalize the modules so that the package can accomodate both positive and negative numbers even with multiplication and division.
Solution.

(7.2) Generalize the modules so that the package produces a suitable error message when it divides by an interval that contains zero.
Solution.

(7.3) Extend the modules so that the local rounding error at the operation is also appropriately dealt with. (This is not the case at the moment.)
Solution.


Last modified: 6 April 1999
boein@nsc.liu.se