Chapter 15
Essentials of Essentials of Fortran-90/95/2005
Derived Types (Record Variables)

Math 60 -- D. C. Smolarski, S.J.
Santa Clara University, Department of Mathematics and Computer Science

[Return to Math 60 Homepage | Return to Brief Contents Page]
[Return to Full Contents Page]

Contents


15.1 Overview

Fortran-90/95/2003 includes the concept of record or structured variables through what is called a derived type. These are variables which have subsections, often called "fields" or "components." The entire record may be referenced and given a name as a single unit or, alternatively, any component of the record may be referenced and named.

As in other languages, when using record variables, one defines the new structured data type first and gives it a name, and then declares variables of that new data type.

15.2 Defining a Derived Type

A new derived type is defined using the keyword TYPE followed by the name of the new structured type, and ending the structure definition with END TYPE. The name of the derived type is any legal Fortran identifier. After the type header line, components of the new type are declared in the standard way that any variable is declared.

For example, to define a new type named INFOLINE with four components, one would write the following:

        TYPE INFOLINE
	   CHARACTER (LEN = 30) :: NAME
	   CHARACTER (LEN = 50) :: ADDRESS
	   INTEGER :: ZIP
	   INTEGER :: ACCNTNUM
	END TYPE INFOLINE
The first two of the four components are character sections (with names NAME and ADDRESS), and the last two are integers (with names ZIP and ACCNTNUM).

Fortran-90 does not allow the initialization of components of a derived type to a specific value, but Fortran-95/2003 permits such an initialization.

15.3 Declaring Variables of a Derived Type

After a new type has been defined, variables of that type may be declared in the usual way, indicating the derived type by a statement beginning with TYPE (type_name).

The following example declares variables of the derived data type defined in the previous section.

        TYPE (INFOLINE) :: ACCOUNT, LIST(100), TEMP
Note that the second variable listed, LIST(100), is an array of 100 elements of type INFOLINE. Each of the 100 elements has the four fields that are part of the new type.

15.4 Accessing Components of Derived Type Variable

To access a field of a derived type variable, in general, the principles are the same as with C++ or Pascal, except that, instead of using the period (dot), . , as a subdivider (or "component selector"), one uses the percent sign, %. For example,
        ACCOUNT%ZIP
indicates the ZIP component of variable ACCOUNT and
	LIST(1)%ACCNTNUM
indicates the ACCNTNUM component of the array element LIST(1). As in other languages, one uses the component part in accordance with its individual type definition. That is, if a component is an integer, such as ACCOUNT%ZIP, one uses it in the same way any integer would be used.

It is also permitted to assign a value to an entire structured variable by indicating the defined type on the right side of an assignment statement followed by values for each field in the variable enclosed in parentheses. For example, suppose we have defined a new type, called PERSON_INFO, which has components called NAME and PHONE, according to the following definition:

   TYPE PERSON_INFO
      CHARACTER (LEN = 30) :: NAME
      CHARACTER (LEN = 10) :: PHONE
   END TYPE PERSON_INFO
Assume the following declarations as well:
   TYPE (PERSON_INFO) :: SAM
   CHARACTER*30 :: TEMPNAME
Then, assuming that TEMPNAME has been given a value, the following assignment is legal:
   SAM = PERSON_INFO(TEMPNAME, '4085551212')

15.5 Using Modules for Type Definition

If a program consists of single, main program segment and contains a derived type, the type definition must come before any variable of that type is declared. Thus, as a general rule, type definitions occur immediately after the segment header statement and before any variable declarations.

In practice, however, especially when using variables of the same derived type in more than one program segment, it is best to include the type definition in a module. This module can then be invoked by each program segment that uses variables of that user-defined type. (For more information about modules, see Chapter 9.)

It is also permitted, within a module, to designate some components of a derived type as PRIVATE, as the example in the next section shows. This may be done when components of a derived type are meant to be accessed only by module procedures, which themselves are PUBLIC. Note also that even though the components of a derived type may be designated as PRIVATE, the derived type itself (by default) remains a public entity and may be used whenever the module is imported in a program segment. Unless specifically designated as PRIVATE, all elements in a module are PUBLIC.

One may change the default attribute by merely listing the keyword PRIVATE in a module before any variables are declared. One can also specify certain declared identifiers to be PUBLIC or PRIVATE by listing them after the keyword in a declaration-type statement or by declaring the identifiers in a type declaration statement with PUBLIC or PRIVATE as an attribute. For example,

      MODULE EXAMPLE1
            PRIVATE
	    REAL :: A, B, C
	    REAL, PUBLIC :: X, Y, Z
	    INTEGER :: I, J, K
	    PUBLIC :: I, C
      CONTAINS
            SUBROUTINE SUB1
	    ...
	    END SUBROUTINE
      END MODULE
Here, A, B, J, K are all private because the
default attribute for the module was changed by the initial
PRIVATE statement (and the default was not
overridden via a specification statement). In contrast, 
X, Y, Z, I, C are public (and therefore directly accessible
to any code making use of this module) because they
were explicitly given the attribute of PUBLIC when
declared or via the specification statement, thus overriding
the default.

15.6 Modules and Data Encapsulation

A major concern of contemporary programming style is the protection of data structures from unintended and unplanned modification. The practice of placing data structures within a larger structure, thereby protecting the data, and allowing the information to be changed only via specific subprograms, is commonly called data encapsulation. Data encapsulation is foundational for "object-oriented programming" since it enables programmers to write code in which data has been protected and in which data is accessed only by specified subprograms.

It is possible to use modules to define a derived type and also encapsulate the key components of the new type, protecting these componenets from unintended changes. This is most easily done by defining a derived type with private data components within a module, and also including within the module local subprograms which automatically have access to the private data components of the derived type.

As an example, the following shows one approach for implementing in Fortran-90/95/2003 the data structure referred to as a stack. The underlying data structure is part of a derived type and this type and associated subprograms are placed in a module called STACK_MOD. The derived type is called STACK and its data, the array STACKELEMENT which will contain the contents of the stack, and the top-of-stack pointer TOP will be PRIVATE, and therefore inaccessible to any program segment which makes use of the derived type.

The module also contains three local routines: PUSH, which adds elements to the stack; POP, which deletes the element on the top of the stack; and INIT, which initializes a new stack by correctly setting the TOP variable to 0. (This last routine is needed in Fortran-90, since initializing a component of a derived type is not allowed in Fortran-90, but this feature has been included in Fortran-95/2003.)

        MODULE STACK_MOD
          TYPE STACK
             PRIVATE
              INTEGER :: STACKELEMENT(100)
              INTEGER :: TOP
           END TYPE
        CONTAINS
          SUBROUTINE INIT(S)
          TYPE(STACK) :: S
          S%TOP = 0
          END SUBROUTINE
	  !
          SUBROUTINE PUSH(S,ITEM)
          TYPE(STACK) :: S
	  INTEGER :: ITEM
          IF (S%TOP < 100) THEN
             S%TOP=S%TOP+1
             S%STACKELEMENT(S%TOP) = ITEM
	  ELSE
             WRITE(*,*) "STACK OVERFLOW"
             STOP
	  ENDIF
	  END SUBROUTINE
	  !
          INTEGER FUNCTION POP(S)
          TYPE(STACK) :: S
          IF (S%TOP > 0) THEN
            POP = S%STACKELEMENT(S%TOP)
            S%TOP = S%TOP-1
          ELSE
            WRITE(*,*) "STACK UNDERFLOW"
	    STOP
          ENDIF
          END FUNCTION
        END MODULE
The following program makes use of the STACK_MOD module and declares two stacks, S and T. It then pushes a few values onto the two stacks and pops the values off to print them out. (For further information about stacks, see a complete book on Data Structures.)
	PROGRAM TESTSTACK
	USE STACK_MOD
	TYPE(STACK) S,T
	INTEGER TEMP
	! Initial both stackpointers
	CALL INIT(S)
	CALL INIT(T)
	! Push several values onto the stacks
	CALL PUSH(S,3)
	CALL PUSH(S,4)
	CALL PUSH(S,5)
	CALL PUSH(T,1)
	CALL PUSH(T,2)
	! Pop a value off and save it in TEMP
	! before printing it out
	TEMP = POP(S)
	WRITE(*,*) TEMP
	! Pop off values and print out directly
	WRITE(*,*) POP(S)
	WRITE(*,*) POP(S)
	WRITE(*,*) POP(T)
	WRITE(*,*) POP(T)
	END


This page is maintained by Dennis C. Smolarski, S.J. dsmolarski@math.scu.edu
© Copyright 1999-2005 Dennis C. Smolarski, S.J., All rights reserved.
Last changed: 27 June 2005.