Модули и программы#

Использование модулей является предпочтительным способом создания современных библиотек и приложений на языке Fortran. По соглашению, один файл исходного кода всегда должен содержать только один модуль, а имя модуля должно соответствовать относительному пути к файлу, чтобы обеспечить удобную навигацию в больших проектах. Также рекомендуется добавлять к имени модуля префикс в виде имени библиотеки, чтобы избежать конфликта имён при использовании библиотеки в качестве зависимости в других проектах.

Пример такого файла модуля приведён здесь

!> Interface to TOML processing library.
!>
!> ...
module fpm_toml
  use fpm_error, only : error_t, fatal_error, file_not_found_error
  use fpm_strings, only : string_t
  use tomlf, only : toml_table, toml_array, toml_key, toml_stat, get_value, &
    & set_value, toml_parse, toml_error, new_table, add_table, add_array, &
    & toml_serializer, len
  implicit none
  private

  public :: read_package_file
  public :: toml_table, toml_array, toml_key, toml_stat, get_value, set_value
  public :: new_table, add_table, add_array, len
  public :: toml_error, toml_serializer, toml_parse

contains

  !> Process the configuration file to a TOML data structure
  subroutine read_package_file(table, manifest, error)
    !> TOML data structure
    type(toml_table), allocatable, intent(out) :: table
    !> Name of the package configuration file
    character(len=*), intent(in) :: manifest
    !> Error status of the operation
    type(error_t), allocatable, intent(out) :: error
    ! ...
  end subroutine read_package_file

end module fpm_toml

В этом примере модуля можно выделить несколько моментов. Во-первых, каждый модуль начинается с комментариев, документирующих цель и содержание модуля. Аналогично, каждая процедура начинается с комментария, кратко описывающего ее назначение и смысл фиктивных аргументов. Документирование – одна из самых важных частей создания долговечного программного обеспечения, независимо от языка.

Во-вторых, импорты (use) и экспорты (public) указаны в явном виде, что позволяет с первого взгляда на исходный текст модуля проверить используемые и доступные процедуры, константы и производные типы. Импорт обычно ограничивается областью применения модуля, а не реимпортируется в каждую процедуру или область применения интерфейса. Аналогично, экспорт осуществляется в явном виде путем добавления оператора private в отдельной строке и явного перечисления всех экспортируемых символов в операторах public.

Наконец, оператор implicit none работает для всего модуля, и нет необходимости дублировать его в каждой процедуре.

Переменные внутри модуля являются статическими (неявно сохранёнными, implicitly saved). Настоятельно рекомендуется ограничить использование переменных модуля только константными выражениями, такими как параметры или перечисления, или экспортировать их как protected, а не как public.

Подмодули (Submodules) можно использовать для разрыва длинных цепочек зависимостей и сокращения каскадов перекомпиляции в программах Fortran. Они также дают возможность предоставлять специализированные и оптимизированные реализации, не требуя использования препроцессора.

Примером из Fortran standard library является модуль квадратур, который определяет только интерфейсы для процедур модуля, но не их реализацию

!> Numerical integration
!>
!> ...
module stdlib_quadrature
  use stdlib_kinds, only: sp, dp, qp
  implicit none
  private

  public :: trapz
  ! ...

  !> Integrates sampled values using trapezoidal rule
  interface trapz
    pure module function trapz_dx_dp(y, dx) result(integral)
      real(dp), intent(in) :: y(:)
      real(dp), intent(in) :: dx
      real(dp) :: integral
    end function trapz_dx_dp
    module function trapz_x_dp(y, x) result(integral)
      real(dp), intent(in) :: y(:)
      real(dp), intent(in) :: x(:)
      real(dp) :: integral
    end function trapz_x_dp
  end interface trapz

  ! ...
end module stdlib_quadrature

В то время как реализация предоставляется в отдельных подмодулях, как, например, для описания интегрирования методом трапеций, приведённого здесь.

!> Actual implementation of the trapezoidal integration rule
!>
!> ...
submodule (stdlib_quadrature) stdlib_quadrature_trapz
  use stdlib_error, only: check
  implicit none

contains

  pure module function trapz_dx_dp(y, dx) result(integral)
    real(dp), intent(in) :: y(:)
    real(dp), intent(in) :: dx
    real(dp) :: integral
    integer :: n

    n = size(y)
    select case (n)
    case (0:1)
      integral = 0.0_dp
    case (2)
      integral = 0.5_dp*dx*(y(1) + y(2))
    case default
      integral = dx*(sum(y(2:n-1)) + 0.5_dp*(y(1) + y(n)))
    end select
  end function trapz_dx_dp

  ! ...
end submodule stdlib_quadrature_trapz

Обратите внимание, что процедуры модуля не обязательно должны быть реализованы в одном подмодуле. Несколько подмодулей могут быть использованы для снижения нагрузки компиляции для больших модулей.

Наконец, при создании программы рекомендуется свести к минимуму количество фактических реализаций в теле программы. Повторное использование реализаций из модулей позволяет написать многократно используемый код и сосредоточиться в программном блоке на передаче пользовательского ввода соответствующим библиотечным функциям и объектам.