The diagram in Figure 1 shows the major components of Fortran 90 [1],[2]. The size of each slice of this ``pie" is roughly proportional to the number of syntax rules needed to describe the features associated with that slice, and hence is a measure of the structural complexity of those features. (These measures should not, however, be taken as an indication of conceptual or semantic complexity nor of implementation effort-syntactic complexity may or may not be related to these other forms of complexity.)
SELECT CASE (expression)
CASE (value-list)
...
CASE (value-list)
...
...
END SELECT
provides ``parallel" selection control. It offers no increase
in semantic power over the IF (``sequential") selection
control, but in situations where it fits the problem CASE
offers computational advantages over IF because only one
expression is evaluated.
The new DO construct is likely to be extremely useful in computational science applications. The reason is that, in order to realize the computational benefits of data parallelism, a great many indexed do loops will be replaced by array operations. The emphasis in the use of loops will therefore shift from processing arrays to indefinite repetition operations such as ``read-test-process" and ``while" repetition. The two new forms of DO construct added in Fortran 90 therefore are:
DO DO WHILE (logical-expr)
... ...
IF (logical-expr) EXIT END DO
...
END DO
Indexed loops may also be terminated with END DO, in which
case the loop label is not needed. (Loop labels are
permitted, however, in all three forms of DO construct.)
Because of the adverse impact that pointers have on optimization, a Fortran 90 pointer may point only to (a) a data object explicitly declared as a pointer target, (b) a dynamically created object, or (c) another pointer. This makes it possible for static storage optimization technology to be be applied to data that has neither the pointer nor target attributes.
Such pointers may be used for arrays whose sizes are determined at run time, such as dynamically allocated arrays; such arrays are declared as ``deferred shape" arrays, in which the rank is specified, and thereby fixed, but the extent in each dimension is unspecified and dynamic. These dimensions will be dynamically established later. The following examples illustrate such dynamic behavior. (The ``=>" is the Fortran 90 syntax for pointer assignment, and the ALLOCATE statement is used for dynamic allocation of storage.)
REAL, TARGET :: B(100,100) ! Array B has the target attribute.
REAL, POINTER :: U(:,:),V(:),W(:,:) ! Declaration of 3 pointer arrays
...
U => B(I:I+2,J:J+2) ! U points to a 3x3 section of B.
ALLOCATE ( W(M,N) ) ! Dynamically allocate W, size MxN
V => B(:,J) ! V points to the Jth column of B.
V => W(I-1,1:N:2) ! V changed to point to (part of)
! the I-1st row of W.
This is an example of a type that could be used for a doubly-liked list structure. Recursive components (PREVIOUS and NEXT in this example) must be pointers. In general, a defined type may have any number and types of components.
TYPE LIST REAL :: DATA TYPE(LIST),POINTER :: PREVIOUS, NEXT END TYPE LIST
TYPE RATIONAL ! This defines the type RATIONAL.
INTEGER :: NUMERATOR
INTEGER :: DENOMINATOR
END TYPE RATIONAL
...
TYPE (RATIONAL) :: X, Y(100,100) ! X and Y are variables of
! type RATIONAL.
This might be the type defined in connection with a complete rational arithmetic data abstraction. Such an abstraction requires, in addition to the type definition, an appropriate set of operator definitions. These are done by specifying operator interfaces for user-defined functions. The following example illustrates extending the ``+" operator to addition between objects of type RATIONAL.
INTERFACE OPERATOR (+) FUNCTION RAT_ADD(X,Y) TYPE (RATIONAL) :: RAT_ADD TYPE (RATIONAL) :: X, Y END FUNCTION RAT_ADD END INTERFACE
More details on user-defined types and operators will be discussed in sections of the next release of this chapter.
An important concept new to Fortran 90 is that of an explicit procedure interface. This means that the interface of a procedure-that is, the data types and other characteristics of the dummy arguments and (if the procedure is a function) function result-is known at the point of call. Explicit interfaces provide a long list of benefits and capabilities; for example, a function result can be array valued if (and only if) the function interface is explicit where the function is called. Procedure interfaces may be made explicit by providing an interface block for the procedure or by placing the procedure definition internal to the calling program or in a module used by the calling program.
One of the most onerous sources of programming errors historically has been the mismatching of argument data types across procedure boundaries. Such errors, guaranteed to corrupt results, are among the hardest to debug. Use of explicit interfaces is simple and prevents this problem completely; this will likely be one of the most important practical applications of interface blocks.
Unlike main programs and external subprograms, modules are not themselves executable program units. Rather, they contain definitions that can be conveniently accessed and used by executable program units. For example, a module might contain interface blocks for a library of external procedures and be used to make the interfaces of these library procedures explicit in an application using that library. A likely growing trend is to repackage the entire library in a module-that is, to place all of the procedure definitions in a module (assuming the procedures can be written in Fortran)-which has the twin benefits to an application of making the procedure interfaces explicit without the need for interface blocks and giving the application developer a powerful tool for namespace management.
Data related uses of modules are just as important as procedure related uses. User-defined type definitions can be placed in a module and thus made available to the other program units of an application , and indeed this is the preferred way of packaging most type definitions. Data objects, of any type, kind, and shape can be declared in a module and thus become global data objects for an application using that module. This provides a non- storage-associated global data alternative to COMMON, which can be used where storage association is a problem (e.g., in distributed memory environments). Arrays in modules can be allocatable, thus providing a means of having dynamic arrays conveniently accessible to any of the program units of the application.
A module can simultaneously contain both data related and procedure related entities. One typical such application of a module is to package a data abstraction- that is, to contain a type definition, interfaces defining operators to be used in conjunction with operands of that type, and possibly even the procedures defining operations on that type. For example the type definition for RATIONAL and the extension of ``+" to RATIONAL addition illustrated above could be packaged in a module as follows:
MODULE RATIONAL_ARITHMETIC TYPE RATIONAL INTEGER :: NUMERATOR INTEGER :: DENOMINATOR END TYPE RATIONAL INTERFACE OPERATOR (+) FUNCTION RAT_ADD(X,Y) TYPE (RATIONAL) :: RAT_ADD TYPE (RATIONAL) :: X, Y END FUNCTION RAT_ADD END INTERFACE ... ! and other stuff for a complete rational arithmetic facility END MODULE RATIONAL_ARITHMETICThis sort of comprehensive use of modules for will be described in more detail in the next release.
The other major I/O addition in Fortran 90 is nonadvancing I/O, which provides the functionality of reading or writing only part of a record. Recall that each formatted sequential READ or WRITE, the only form of I/O to which nonadvancing applies, in Fortran 77 causes (at least) one entire record to be read or written; that is, the file is always positioned between records after execution of a READ or WRITE statement. Non advancing I/O is different in that the file remains positioned after the last character transferred (effectively between characters in a record rather than between records)- a nonadvancing READ does not ``skip" to the end of the record and a nonadvancing WRITE does not terminate the record (i.e., does not write an end-of-record mark). The next READ or WRITE on that file starts where the last one left off. Nonadvancing I/O is specified by including ADVANCE='NO' in the control list of the READ or WRITE statement. For example, the following nonadvancing READ statement could be used to obtain the next character from the input terminal:
CHARACTER :: C ... READ(*,`(A)',ADVANCE='NO',IOSTAT=IOS) C ...
Other forms of input/output considered for Fortran 90, but then not adopted, were asynchronous and parallel I/O and a variation of keyed access. Nor is there any direct database support in Fortran 90, though Fortran bindings exist to modern database facilities such as the structured query language (SQL) standard.
The following ten features of Fortran 90 are listed as obsolescent: