Fortran

Guide To Learn

Defining, declaring, and initializing derived types

Perhaps the greatest strength of an object-oriented approach to development is that it offers a mechanism that captures a model of the real world.

  –Grady Booch (1986)

Let’s start with a small program that makes use of a derived type. We’ll define a Person type and assign to it a greeting subroutine, which will do nothing more than print the greeting message to the screen. We’ll then declare an instance of the new type and call the greeting method. Compiling and running this program will yield

gfortran hello_derived_types.f90 -o hello_derived_types
./hello_derived_types
 Hello, my name is Jill!

Let’s dive straight in. The following listing provides the complete code for the hello_ derived_types program.

Listing 8.2 A hello world program expressed using a derived type

module mod_person
  type :: Person                                  ❶
    character(len=20) :: name                     ❷
  contains                                        ❸
    procedure, pass(self) :: greet                ❹
  end type Person                                 ❺
contains
  subroutine greet(self)
    class(Person), intent(in) :: self             ❻
    print *, 'Hello, my name is ' // trim(self % name) // '!'
  end subroutine greet
end module mod_person
 
program hello_derived_types
  use mod_person, only: Person                    ❼
  implicit none
  type(Person) :: some_person = Person('Jill')    ❽
  call some_person % greet()                      ❾
end program hello_derived_types

❶ Opens a new type definition

❷ Type will have only this component (a variable)

❸ Separates components from methods

❹ Type will have only this method (a procedure)

❺ Closes the type definition block

❻ Type-bound procedure has the type itself as input argument

❼ Imports the type from the module

❽ Declares and instantiates the type

❾ Invokes the greet method

Quite a few new things are going on in this program:

  1. We defined a simple Person type, with a character(len=20) name as a component.
  2. We also specified that a Person type contains a procedure, greet, defined in the same module. This way, we effectively bound (attached) this procedure to the Person type.
  3. In the main program, we imported the Person type from the module, declared a new instance, some_person, and initialized it as Person('Jill').
  4. Finally, we called this instance’s greeting method, some_person % greet().

Each of these steps includes at least one new language or syntax element, so let’s take it slowly as we go over each one. The first thing to get used to is some new terminology. I’ll be talking a lot about classes, instances, components, and methods. These are all closely related to the so-called object-oriented programming (OOP) style. If you’re familiar with OOP from some other language, feel free to skim through, or jump straight ahead to the next section.

These are the specific terms that we’ll adopt in this chapter:

  • Class –I’ll use the terms class and derived type interchangeably. The word class is commonly used in general programming and computer science lingo, while derived type is a Fortran-specific term. In OOP terminology, a class is a recipe for creating objects.
  • Instance –Once you define a class (or import its definition from a module), you can declare as many different instances of that class as you want. The word instance thus always refers to a concrete realization of a class in the program, an object.
  • Component –What’s unique about classes is that they can have any number of variables of any type, be it numeric types, such as integer or real, scalar or array, or even an instance of the same or some other class. I’ll refer to all these as class components. In short, a component is what a class has.
  • Method –Much like we can define variables as components of a class, we can bind procedures (functions or subroutines) to a class to two great effects. First, they always come with the class instance and don’t need to be imported separately. Second, methods have access to all of a class’s components and methods. In simple terms, a method is what a class does.

At first, if you look at listing 8.2, you may protest and say, “Hey, this is so much code and complexity for just a simple greeting message!” I hear you, and I agree that this is a trivial example. However, a simple example like this will help us understand how derived types work at their core, and for what kind of problems they may be useful. Bear with me, and soon you’ll see some of the powerful capabilities of this approach. As we work through this chapter, we’ll use these tools to refactor the tsunami solver and extend it to two dimensions.

At the same time, notice that a derived type is yet another layer of abstraction over fundamental data types. Introducing it can be justified only if its benefits outweigh the cost. In this case, the benefit is that if a more complex data structure will be used often, using a derived type can do away with much of the boilerplate code. The cost is associated with added complexity and opaqueness. A casual reader may ask, “What’s inside this Person instance?” or, “Does Person % greet() really just print a message to the terminal, or does it have other side effects?” You get the idea. Figure 8.3 illustrates the most basic use of a derived type.

Figure 8.3 Defining, declaring, initializing, and invoking a method of a derived type

Note A Fortran derived type is analogous to a C struct or a Python or JavaScript class.

Defining, declaring, and initializing derived types

Leave a Reply

Your email address will not be published. Required fields are marked *

Scroll to top