Generic Programming & Operator Overloading

Fortran (until 202Y templates) lacks true generics.

23.1 Generic interfaces (overloading by argument types)

module swap_mod
  implicit none
  interface swap
    module procedure swap_int, swap_real, swap_char
  end interface swap
contains
  subroutine swap_int(a, b)
    integer, intent(inout) :: a, b
    integer :: t; t = a; a = b; b = t
  end subroutine
  subroutine swap_real(a, b)
    real, intent(inout) :: a, b
    real :: t; t = a; a = b; b = t
  end subroutine
  subroutine swap_char(a, b)
    character(len=*), intent(inout) :: a, b
    character(len=len(a)) :: t; t = a; a = b; b = t
  end subroutine
end module swap_mod
! call swap(i, j); call swap(x, y)

23.2 Operator overloading

module vector_mod
  use, intrinsic :: iso_fortran_env, only: dp => real64
  implicit none
  type :: vec
    real(dp) :: x = 0, y = 0, z = 0
  contains
    procedure, private :: add, sub, scale_left, dot
    generic :: operator(+) => add
    generic :: operator(-) => sub
    generic :: operator(*) => scale_left
    generic :: operator(.dot.) => dot          ! user-defined operator
  end type vec

  interface operator(==)
    module procedure vec_equal
  end interface

  interface assignment(=)
    module procedure assign_from_array
  end interface
contains
  pure type(vec) function add(a, b);  class(vec), intent(in) :: a, b
    add = vec(a%x + b%x, a%y + b%y, a%z + b%z); end function
  pure type(vec) function sub(a, b);  class(vec), intent(in) :: a, b
    sub = vec(a%x - b%x, a%y - b%y, a%z - b%z); end function
  pure type(vec) function scale_left(a, k); class(vec), intent(in) :: a; real(dp), intent(in) :: k
    scale_left = vec(a%x * k, a%y * k, a%z * k); end function
  pure real(dp) function dot(a, b);   class(vec), intent(in) :: a, b
    dot = a%x * b%x + a%y * b%y + a%z * b%z; end function
  pure logical function vec_equal(a, b); type(vec), intent(in) :: a, b
    vec_equal = a%x
…

23.3 Generic programming techniques (pre-templates)

Fortran (until 202Y templates) lacks true generics. Common approaches:

Generic interfaces with one specific procedure per type/kind.

Preprocessor / include templates: write the code once with macros, include it for each type.

fypp (Python-based Fortran preprocessor) — used by stdlib to generate kind-generic code:

**class(*)** unlimited polymorphism with select type (runtime generic).

Fortran 202Y template/requirement/instantiate (coming).

#:for k in ['sp', 'dp']
  pure function mean_${k}$(x) result(m)
    real(${k}$), intent(in) :: x(:)
    real(${k}$) :: m
    m = sum(x) / size(x)
  end function
#:endfor

23.4 Assumed-rank & select rank (F2018)

subroutine describe(a)
  real, intent(in) :: a(..)
  select rank (a)
  rank (0); print *, "scalar", a
  rank (1); print *, "vector of size", size(a)
  rank (2); print *, "matrix", shape(a)
  rank default; print *, "rank", rank(a)
  end select
end subroutine describe