Módulos e Programas#

Módulos é o modo preferido para criar bibliotecas e aplicações Fortran modernas. Como uma convenção, um arquivo de fonte única deve sempre conter apenas um módulo, enquanto que o nome do módulo deve sempre corresponder ao caminho de arquivo para facilitar a navegação em projetos grandes. É também recomendado prefixar o nome dos módulos com o nome da biblioteca para evitar conflite de nomes quando usando como dependências em outros projetos.

Um exemplo para tal arquivo de módulo é dado aqui

!> 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

H[a algumas coisas nesse exemplo a destacar. Primeiro, cada módulo começa com comentários documentando o propósito e conteúdo do módulo. Semelhante, cada procedimento começa com um breve comentário descrevendo seu propósito e a intenção dos argumentos fictícios. Documentação é uma das partes mais importantes para a criação de software de longa duração, independente da linguagem.

Segundo, imports (use) e exports (public) são passados explicitamente, isso permite uma visão da fonte do módulo para checar os procedimentos usados e disponíveis, tipos constantes e derivadas. Os imports são normalmente limitados ao escopo do módulo ao invés de reimportado em cada escopo de procedimento ou interface. Semelhante, exports são feitos explicitamente adicionando a declaração private em uma única linha e explicitamente listando todos os símbolos exportados na declaração public.

Finalmente, a declaração implicit none funciona para todo o módulo e não há necessidade de repetir dentro de cada procedimento.

Variáveis dentro de módulos são estáticos (implicitly saved). É altamente recomendado limitar o uso de variáveis de módulos para expressões constantes, como unicamente parâmetros ou enumeráveis, ou exportá-los como protected ao invés de public.

Submódulos podem ser usar para quebrar longas cadeias de dependência e encurtar a cascata de recompilação em programas Fortran. Eles também oferecem a possibilidade de fornecer implementações especializadas e otimizadas sem necessidade de uso de pré-processador.

Um exemplo da Fortran standard library é o módulo quadrature, que define somente interfaces para os procedimentos de módulo, mas sem implementações

!> 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

Enquanto que a implementação é fornecida em submódulos separados como esse para a regra de integração trapezoidal.

!> 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

Note que os procedimentos de módulos não tem que ser implementados no mesmo submódulo. Vários submódulos podem ser usados para reduzir a carga de compilação para módulos grandes.

Finalmente, quando configurando um programa, é recomendado que mantenha as reais implementações no corpo programa ao mínimo. Reusar implementações de módulos permite que você escreva código reutilizável e foque a unidade do programa em transmitir o input do usuário para os respectivos objetos e funções da biblioteca.