ZetCode

Fortran operator overloading and generic interfaces

last modified November 14, 2026

A type that represents a vector or a fraction should be as easy to use as an integer. Fortran lets a type overload an intrinsic operator: after an interface for operator(+) is written, a + b calls a function of your own when a and b are values of the type. The same mechanism covers assignment, comparison and named operators such as .dot., and a generic interface gives several procedures one common name. The compiler resolves every call from the types of the arguments, so the notation stays natural while the work is done by ordinary functions with explicit interfaces.

Overloading an intrinsic operator

An operator interface lists the functions that implement the operator for particular argument types. The block interface operator(+) with module procedure add_vec tells the compiler that two values of type vec may be added, and the function add_vec returns the sum. The multiplication interface names two procedures, one for real * vec and one for vec * real, because the operands of * are not interchangeable.

w1.f90
module vecmod
    implicit none
    private
    public :: vec, operator(+), operator(-), operator(*)

    type :: vec
        real :: x = 0.0, y = 0.0
    end type vec

    interface operator(+)
        module procedure add_vec
    end interface

    interface operator(-)
        module procedure sub_vec
    end interface

    interface operator(*)
        module procedure scale_left, scale_right
    end interface
contains
    pure function add_vec(a, b) result(r)
        type(vec), intent(in) :: a, b
        type(vec) :: r

        r = vec(a%x + b%x, a%y + b%y)
    end function add_vec

    pure function sub_vec(a, b) result(r)
        type(vec), intent(in) :: a, b
        type(vec) :: r

        r = vec(a%x - b%x, a%y - b%y)
    end function sub_vec

    pure function scale_left(k, v) result(r)
        real, intent(in) :: k
        type(vec), intent(in) :: v
        type(vec) :: r

        r = vec(k * v%x, k * v%y)
    end function scale_left

    pure function scale_right(v, k) result(r)
        type(vec), intent(in) :: v
        real, intent(in) :: k
        type(vec) :: r

        r = vec(v%x * k, v%y * k)
    end function scale_right
end module vecmod

program w1
    use vecmod
    implicit none
    type(vec) :: a, b, c

    a = vec(1.0, 2.0)
    b = vec(3.0, -1.0)
    c = a + b
    print *, 'a + b: ', c
    c = a - b
    print *, 'a - b: ', c
    c = 2.0 * a
    print *, '2 * a: ', c
    c = b * 0.5
    print *, 'b * 0.5:', c
end program w1

The sums and differences are computed component by component, and the scalar multiplications give the expected vectors. Without the second procedure in the multiplication interface, 2.0 * a would be a compile-time error while a * 2.0 would work, which is a common surprise. The interfaces are declared public in a module that is otherwise private, so the operators are visible to the program exactly like the type itself.

$ gfortran w1.f90 -o w1
$ ./w1
 a + b:    4.00000000       1.00000000    
 a - b:   -2.00000000       3.00000000    
 2 * a:    2.00000000       4.00000000    
 b * 0.5:   1.50000000     -0.500000000    

Defined assignment

Assignment can be overloaded in the same way, through interface assignment(=). A defined assignment is used when the types on the two sides differ from the declared type of the variable, and its first dummy argument is the target, declared intent(out), while the second is the source. The fraction type below accepts an integer or a real on the right-hand side, reduces the result to lowest terms with Euclid's algorithm, and compares two fractions by cross multiplication.

w2.f90
module fracmod
    implicit none
    private
    public :: fraction, operator(*), operator(==), assignment(=), show

    type :: fraction
        integer :: num = 0
        integer :: den = 1
    end type fraction

    interface assignment(=)
        module procedure frac_from_int, frac_from_real
    end interface

    interface operator(*)
        module procedure mul_fractions, mul_by_int
    end interface

    interface operator(==)
        module procedure equal_fractions
    end interface
contains
    pure subroutine frac_from_int(self, n)
        type(fraction), intent(out) :: self
        integer, intent(in) :: n

        self = fraction(n, 1)
    end subroutine frac_from_int

    pure subroutine frac_from_real(self, r)
        type(fraction), intent(out) :: self
        real, intent(in) :: r

        self = reduce(nint(r * 10000.0), 10000)
    end subroutine frac_from_real

    pure function mul_fractions(a, b) result(r)
        type(fraction), intent(in) :: a, b
        type(fraction) :: r

        r = reduce(a%num * b%num, a%den * b%den)
    end function mul_fractions

    pure function mul_by_int(a, n) result(r)
        type(fraction), intent(in) :: a
        integer, intent(in) :: n
        type(fraction) :: r

        r = reduce(a%num * n, a%den)
    end function mul_by_int

    pure logical function equal_fractions(a, b) result(flag)
        type(fraction), intent(in) :: a, b

        flag = a%num * b%den == b%num * a%den
    end function equal_fractions

    pure function reduce(n, d) result(r)
        integer, intent(in) :: n, d
        type(fraction) :: r
        integer :: g

        g = gcd(abs(n), abs(d))
        if (g == 0) g = 1
        r = fraction(n / g, d / g)
        if (r%den < 0) r = fraction(-r%num, -r%den)
    end function reduce

    pure integer function gcd(a, b) result(g)
        integer, intent(in) :: a, b
        integer :: x, y, t

        x = a
        y = b
        do while (y /= 0)
            t = mod(x, y)
            x = y
            y = t
        end do
        g = x
    end function gcd

    subroutine show(f)
        type(fraction), intent(in) :: f

        print '(a, i0, a, i0)', 'fraction ', f%num, '/', f%den
    end subroutine show
end module fracmod

program w2
    use fracmod
    implicit none
    type(fraction) :: f, g, h

    f = 0.25
    g = 3
    call show(f)
    call show(g)

    h = f * g
    call show(h)
    h = f * 8
    call show(h)
    print *, 'f == 0.25 as fraction:', f == fraction(1, 4)
    print *, 'f == g:              ', f == g
end program w2

The real 0.25 becomes 1/4 and the integer 3 becomes 3/1; multiplying them gives 3/4, and multiplying by eight gives 2/1, already reduced. The conversion from a real multiplies by ten thousand and reduces, so it keeps four decimal places and nothing more; a general fraction would need a different approach. The comparison is exact for the integer components of the type, which is why 1/4 equals a fraction built as fraction(1, 4) even though the two values arrived by different routes.

$ gfortran w2.f90 -o w2
$ ./w2
fraction 1/4
fraction 3/1
fraction 3/4
fraction 2/1
 f == 0.25 as fraction: T
 f == g:               F

One generic name for several procedures

A generic interface is an interface block without an operator: it simply lists procedures and gives them a common name. All the procedures must be either functions or subroutines, and each must be distinguishable from the others by the number, the types or the ranks of its arguments. The block below groups three versions of sort2, one for each type, and a call is resolved from the arguments exactly as an intrinsic procedure would be.

w3.f90
module sorting
    implicit none
    private
    public :: sort2

    interface sort2
        module procedure sort2_int, sort2_real, sort2_char
    end interface
contains
    pure subroutine sort2_int(a, b)
        integer, intent(inout) :: a, b
        integer :: t

        if (a > b) then
            t = a
            a = b
            b = t
        end if
    end subroutine sort2_int

    pure subroutine sort2_real(a, b)
        real, intent(inout) :: a, b
        real :: t

        if (a > b) then
            t = a
            a = b
            b = t
        end if
    end subroutine sort2_real

    pure subroutine sort2_char(a, b)
        character(len=*), intent(inout) :: a, b
        character(len=len(a)) :: t

        if (a > b) then
            t = a
            a = b
            b = t
        end if
    end subroutine sort2_char
end module sorting

program w3
    use sorting
    implicit none
    integer :: i, j
    real :: x, y
    character(len=10) :: s, t

    i = 7
    j = 3
    call sort2(i, j)
    print *, 'integers:', i, j

    x = 2.5
    y = 1.5
    call sort2(x, y)
    print *, 'reals:   ', x, y

    s = 'pear'
    t = 'apple'
    call sort2(s, t)
    print '(a, a, a, a)', 'strings: ', trim(s), ' ', trim(t)
end program w3

The same name sorts two integers, two reals and two strings, and each call reaches the matching procedure at compile time. The string version needs a temporary of the same length as the arguments, which is why it declares character(len=len(a)) :: t rather than a fixed length. A call with a type that none of the procedures accepts is an error, and adding a new version later means adding one line to the interface and one procedure, without changing any existing call.

$ gfortran w3.f90 -o w3
$ ./w3
 integers:           3           7
 reals:      1.50000000       2.50000000    
strings: apple pear

Generic functions over intrinsic and derived types

A generic interface may mix procedures that take intrinsic types with procedures that take derived types, which is how a single mathematical name can cover several kinds of value. The function magnitude below returns the absolute value of a real, the modulus of a complex number, and the length of a two-dimensional point. The three implementations share the name but not a line of code.

w4.f90
module measures
    implicit none
    private
    public :: point, magnitude

    type :: point
        real :: x = 0.0, y = 0.0
    end type point

    interface magnitude
        module procedure mag_real, mag_complex, mag_point
    end interface
contains
    pure real function mag_real(x) result(r)
        real, intent(in) :: x

        r = abs(x)
    end function mag_real

    pure real function mag_complex(z) result(r)
        complex, intent(in) :: z

        r = abs(z)
    end function mag_complex

    pure real function mag_point(p) result(r)
        type(point), intent(in) :: p

        r = sqrt(p%x**2 + p%y**2)
    end function mag_point
end module measures

program w4
    use measures
    implicit none

    print *, 'real:    ', magnitude(-3.5)
    print *, 'complex: ', magnitude((3.0, 4.0))
    print *, 'point:   ', magnitude(point(6.0, 8.0))
end program w4

The real call returns 3.5, the complex number (3, 4) has modulus 5, and the point (6, 8) is at distance 10 from the origin. The complex literal is written with parentheses around the two parts, and the point is built with its structure constructor. Because magnitude is a generic name rather than a type-bound one, it also works for plain values that have no type of their own to belong to.

$ gfortran w4.f90 -o w4
$ ./w4
 real:       3.50000000    
 complex:    5.00000000    
 point:      10.0000000    

Named operators

Besides the intrinsic symbols, Fortran allows operators with names of your own, written between dots. An interface for operator(.dot.) makes a .dot. b a valid expression, and the name is as visible and as checkable as any other operator. The example defines the dot product of two three-dimensional vectors, which returns a real, and the cross product, which returns a vector.

w5.f90
module geometry
    implicit none
    private
    public :: vec3, operator(.dot.), operator(.cross.)

    type :: vec3
        real :: x = 0.0, y = 0.0, z = 0.0
    end type vec3

    interface operator(.dot.)
        module procedure dot_product_vec
    end interface

    interface operator(.cross.)
        module procedure cross_product_vec
    end interface
contains
    pure real function dot_product_vec(a, b) result(r)
        type(vec3), intent(in) :: a, b

        r = a%x * b%x + a%y * b%y + a%z * b%z
    end function dot_product_vec

    pure function cross_product_vec(a, b) result(r)
        type(vec3), intent(in) :: a, b
        type(vec3) :: r

        r = vec3(a%y * b%z - a%z * b%y, &
                 a%z * b%x - a%x * b%z, &
                 a%x * b%y - a%y * b%x)
    end function cross_product_vec
end module geometry

program w5
    use geometry
    implicit none
    type(vec3) :: a, b, c

    a = vec3(1.0, 2.0, 3.0)
    b = vec3(4.0, 5.0, 6.0)
    print *, 'dot:  ', a .dot. b
    c = a .cross. b
    print *, 'cross:', c
    print *, 'orthogonal:', (a .dot. c), (b .dot. c)
end program w5

The dot product of (1, 2, 3) and (4, 5, 6) is 32, and the cross product is (-3, 6, -3). The last line checks that the cross product is perpendicular to both inputs: each dot product with it is zero. A named operator is only a function with two arguments, so it can be tested and documented like one; the dotted name simply lets it appear in infix position.

$ gfortran w5.f90 -o w5
$ ./w5
 dot:     32.0000000    
 cross:  -3.00000000       6.00000000      -3.00000000    
 orthogonal:   0.00000000       0.00000000    

Elemental operators and array expressions

An operator procedure may be declared elemental. It then applies to scalars and, element by element, to arrays of the type, which lets the whole array machinery of Fortran work on values of your own. The comparison below is written with a small tolerance, because two real numbers that were computed in different ways are rarely equal bit for bit. With the elemental operators in place, count, any and findloc accept expressions of the derived type directly.

w6.f90
module pointmod
    implicit none
    private
    public :: point, operator(==), operator(<)

    type :: point
        real :: x = 0.0, y = 0.0
    end type point

    interface operator(==)
        module procedure equal_points
    end interface

    interface operator(<)
        module procedure before_point
    end interface
contains
    elemental logical function equal_points(a, b) result(flag)
        type(point), intent(in) :: a, b

        flag = abs(a%x - b%x) < 1.0e-6 .and. abs(a%y - b%y) < 1.0e-6
    end function equal_points

    elemental logical function before_point(a, b) result(flag)
        type(point), intent(in) :: a, b

        flag = a%x < b%x .or. (abs(a%x - b%x) < 1.0e-6 .and. a%y < b%y)
    end function before_point
end module pointmod

program w6
    use pointmod
    implicit none
    type(point) :: a(4), key
    integer :: hits

    a = [point(1.0, 1.0), point(2.0, 3.0), point(1.0, 1.0), point(0.0, 5.0)]
    key = point(1.0, 1.0)
    hits = count(a == key)
    print *, 'matches:', hits
    print *, 'any match:', any(a == key)
    print *, 'first before (1,1):', findloc(a < point(1.0, 1.0), .true.)
end program w6

Two of the four points equal the key, so count reports 2 and any is true. The last line looks for the first point that is ordered before (1, 1); only (0, 5) qualifies, and it sits at position 4. Without the array constructor and the elemental operators this would be a loop with a hand-written comparison, while the version above reads like the same operation on integers.

$ gfortran w6.f90 -o w6
$ ./w6
 matches:           2
 any match: T
 first before (1,1):           4

Defined input and output

The last form of overloading concerns the read and write statements themselves. A type can declare a write(formatted) binding and a read(formatted) binding, which the runtime calls whenever the type appears in formatted output or input, including print * and write (*, '(dt)'). The subroutine receives the value, the unit, a string that names the context, the list of edit descriptors and the usual iostat and iomsg variables, so it can produce any text it likes and report errors like any other input or output operation. That is how a fraction can print itself as 3/4 instead of showing its two integer components.

Defined input and output are the most intricate part of the feature set and are rarely needed in a first program. The simpler interfaces of this article cover most of the ground: arithmetic, comparison, assignment and a readable name for a group of procedures. When a type must survive a full round trip through a text file, the same goal can also be reached with an ordinary write of the components and an ordinary read that rebuilds the value.

Next steps

The type-bound procedures article shows how an operator can also be declared inside a type, and the inheritance and polymorphism article explains how an overloaded operator can accept values of an extended type. The derived types article introduces the components used by every example here, and the formatted output article explains the edit descriptors that a defined output routine would use.

Source

Fortran 2018 standard (J3/18-007r1)

GNU Fortran manual

Fortran-lang: learning resources

In this article we have overloaded +, - and * for a vector, defined assignment from integers and reals for a fraction, given three procedures one generic name, mixed intrinsic and derived types under magnitude, written the named operators .dot. and .cross., and made array expressions work with elemental comparison operators. The previous article, Fortran inheritance and polymorphism, shows how a type is extended and how its bindings are overridden.

Author

My name is Jan Bodnar, and I am a passionate programmer with extensive programming experience. I have been writing programming articles since 2007. To date, I have authored over 1,400 articles and 8 e-books. I possess more than ten years of experience in teaching programming.

List all Fortran tutorials.