Derived Types
Defining a type
A type declaration lists components between type :: name and end type name. Components may be any intrinsic type, another derived type, arrays, or allocatable data. The type(...) :: var syntax then declares variables, and the % operator selects one component — the dot you know from C structs and Python classes, typed with a percent sign:
type :: weather_station
character(len=32) :: name
real(8) :: latitude, longitude
integer :: altitude
real(8), allocatable :: history(:) ! one reading per day, grows
end type weather_station
Fig. 1 — One type description, many independent instances; assignment copies the value.
Instances and assignment
Each typed variable is a complete record. Assignment copies every component — deep enough that the two variables hold equal values, not shared storage. The structure constructor initializes whole instances positionally or by keyword, and any component may be overwritten afterward with its own assignment:
Arrays of types and nested types
Typed variables compose exactly like intrinsics. An array of stations is a structured list; a station component can itself be a type, building a hierarchy of records. Whole-array operations still apply element-wise — stations%altitude in an expression is an array of the component values, which makes reductions over components one-liners:
program station_array
implicit none
type :: coordinates
real(8) :: lat, lon
end type coordinates
type :: station
character(len=24) :: name
type(coordinates) :: where
integer :: altitude
end type station
type(station) :: net(3)
integer :: i
net(1) = station("Chur", coordinates(46.85d0, 9.53d0), 556)
net(2) = station("Jungfrau", coordinates(46.55d0, 7.98d0), 3571)
net(3) = station("Lugano", coordinates(46.00d0, 8.95d0), 273)
print '(a,i0)', "highest station is #", maxloc(net%altitude, dim=1)
do i = 1, 3
print '(a,2f7.2)', trim(net(i)%name), net(i)%where%lat, net(i)%where%lon
end do
end program station_array
Allocatable components and deep copy
An allocatable component makes the type self-sizing. Assignment of such a type performs a deep copy — the target gets its own allocation with the same contents — so two variables never alias the same history array. This is the property that keeps derived types safe to return from functions and pass by value; a pointer component (the pointers lesson) would share, which is sometimes the point and sometimes the bug:
program deep_copy
implicit none
type :: series
real(8), allocatable :: y(:)
end type series
type(series) :: s1, s2
allocate (s1%y(3)); s1%y = [1.0d0, 2.0d0, 3.0d0]
s2 = s1 ! deep copy: separate allocations
s2%y(1) = 99.0d0 ! changes only s2
print *, s1%y ! 1 2 3 — untouched
print *, s2%y ! 99 2 3
end program deep_copy
Design for extension
Derived types become objects in the oop lesson: procedures bound to the type (procedure :: write => station_write), access control inside modules (private components), and inheritance via type, extends(...). Design rule from day one: keep component data private behind a module and let constructors and type-bound procedures be the only door in — it costs nothing now and makes the oop lesson's machinery natural later.
program station_demo
implicit none
type :: weather_station
character(len=32) :: name = "unnamed"
real(8) :: latitude = 0.0d0, longitude = 0.0d0
integer :: altitude = 0
real(8), allocatable :: history(:)
end type weather_station
type(weather_station) :: a, b
a = weather_station(name="Chur", latitude=46.85d0, longitude=9.53d0)
a%altitude = 556 ! patch one component
allocate (a%history(365))
b = a ! full copy: b is independent
b%name = "Zurich" ! changing b leaves a intact
print '(a,a,1x,i0)', trim(a%name), trim(b%name), a%altitude
end program station_demo