Search code examples
classoopfortrancallable

Is it possible to make a class instance callable in Fortran?


I have a polynomial class TPoly, see the code below, which takes an array of coefficients coefs and evaluates the polynomial at some point x using the eval method. I was wondering whether it is possible to evaluate the polynomial directly by making the instance p callable, i.e. instead of p%eval(x) use p(x). In Python, the desirable behaviour can be achieved by defining the __call__ method. Does something similar exist in Fortran?

I want to make another class which will contain many TPoly attributes and I would like to avoid chaining of component selections object%poly1%eval(x). A function pointer to a class method would solve my problem but I found that it is not possible, see another thread.

module polymod

    implicit none

    integer, parameter :: rp = kind(1.0d0)

    ! polynomial class
    type :: TPoly
        real(rp), allocatable :: coefs(:)
        contains 
        procedure, pass :: eval => eval_poly
    end type

    contains 

    real(rp) function eval_poly(self, x)
        class(TPoly), intent(in) :: self
        real(rp), intent(in) :: x

        eval_poly = polynomial(x, self%coefs)

    end function eval_poly

    ! use Horner's method to evaluate polynomial
    real(rp) function polynomial(x, coefs)
        real(rp), intent(in) :: x, coefs(:)
        real(rp) :: p
        integer :: n, i

        n = size(coefs)
        p = coefs(n)
        do i = n-1, 1, -1
            p = p * x + coefs(i)
        end do
        polynomial = p

    end function polynomial

 
end module polymod


program main
    
    use polymod
    implicit none

    type(TPoly) :: p
    real(rp) :: coefs(3)
    real(rp) :: x = 2.0_rp

    coefs = (/3.0_rp, -1.0_rp, 1.0_rp/)

    p = TPoly(coefs)

    print *, p%eval(x)
    ! I would like to use 
    ! print *, p(x)

end program main


Solution

  • Unfortunately, no, this is not possible. To my knowledge there is no nice workaround which would simulate the behaviour either.

    If it were possible, it would need to avoid conflicting with Fortran's array access syntax. Consider the following:

    program main
      use polymod
      implicit none
      
      ! Declare an array of type TPoly.
      type(TPoly) :: p(1)
      
      ! Initialise the array.
      p = [TPoly(coefs=[3.0_rp, -1.0_rp, 1.0_rp])]
      
      ! Try to print.
      print *, p(1)
    end program
    

    There would be no way for the compiler to distinguish between p(1) referring to the first element of the array p vs p(1) calling the class function as an elemental function acting on every element of p with argument 1.

    You could distinguish the two for non-elemental functions, but you would need some syntax like p(1)(2.0_rp), and such syntax doesn't exist in this context in Fortran as it stands.