'Difference between Fortran's "abstract" and "normal" interfaces

I'm trying to understand the difference between abstract interfaces and "normal" interfaces. What makes an interface abstract? When is each one necessary?

Suppose the examples below

module abstract_type_mod
  implicit none

  type, abstract :: abstract_t
  contains
    procedure(abstract_foo), pass, deferred :: Foo
  end type

  interface
    subroutine abstract_foo ( this, a, b )
      import :: abstract_t
      implicit none
      class(abstract_t), intent(in)  :: this
      real,              intent(in)  :: a
      real,              intent(out) :: b
    end subroutine
  end interface

end module

module concrete_type_mod
  use abstract_type_mod
  implicit none

  type, extends ( abstract_t ) :: concrete_t
  contains
    procedure, pass :: Foo
  end type

  contains

  subroutine Foo ( this, a, b )
    implicit none
    class(concrete_t), intent(in)  :: this
    real,              intent(in)  :: a
    real,              intent(out) :: b

    b = 2 * a

  end subroutine
end module 

module ifaces_mod
  implicit none

  interface
    subroutine foo_sub ( a, b )
      implicit none
      real, intent(in)  :: a
      real, intent(out) :: b
    end subroutine
  end interface

end module

module subs_mod
  implicit none

  contains

  pure subroutine module_foo ( a, b )
    implicit none
    real, intent(in)  :: a
    real, intent(out) :: b

    b = 2 * a

  end subroutine

end module

program test
  use ifaces_mod
  use subs_mod
  use concrete_type_mod
  implicit none

  type(concrete_t) :: concrete
  procedure(foo_sub) :: external_sub
  procedure(foo_sub), pointer :: foo_ptr
  real :: b

  foo_ptr => external_sub

  call foo_ptr ( 0.0, b )
  print*, b

  foo_ptr => module_foo

  call foo_ptr ( 1.0, b )
  print*, b

  call concrete%Foo ( 1.0, b )
  print*, b

end program

pure subroutine external_sub ( a, b )
  implicit none
  real, intent(in)  :: a
  real, intent(out) :: b

  b = a + 5

end subroutine

The output is

5.000000
2.000000
2.000000

I haven't used abstract interfaces here. At least I think I havent? I've been doing this for a while and I've never used the abstract "qualifier" on interfaces. It seems that I haven't found a case where using abstract interfaces is required.

Could someone enlighten me here?

PS: Compiler Intel Visual Fortran Composer XE 2013 SP1 Update 3.


Edit:

Quoting Metcalf, Reid and Cohen in Modern Fortran Explained:

In Fortran 95, to declare a dummy or external procedure with an explicit interface, one needs to use an interface block. This is fine for a single procedure, but is somewhat verbose for declaring several procedures that have the same interface (apart from the procedure names). Furthermore, in Fortran 2003, there are several situations where this becomes impossible (procedure pointer components or abstract-type bound procedures).

So, is my compiler in error for accepting the code below and also the one with abstract type above?

module ifaces_mod
  implicit none

  interface
    subroutine foo_sub ( a, b )
      implicit none
      real, intent(in)  :: a
      real, intent(out) :: b
    end subroutine
  end interface

end module

module my_type_mod
  use ifaces_mod
  implicit none

  type my_type_t
    procedure(foo_sub), nopass, pointer :: Foo => null()  
  end type

end module

In both cases, I'd say that I actually have declared abstract interfaces without using the abstract keyword. I think my confusion has roots on the fact that the compiler is accepting code like this.



Solution 1:[1]

The "normal" interfaces — known by the standard as specific interface blocks (as you use in the title of the question) — are just normal interface blocks for some procedure. Therefore:

interface
  subroutine foo_sub
  end subroutine
end interface

means that there exists an actual (external) subroutine named foo_sub and it conforms to the specified interface.

An abstract interface

abstract interface
  subroutine foo_sub_abs
  end subroutine
end interface

just specifies how some procedure may look like, but the name is the name of the interface, not of any actual procedure. It can be used for a procedure pointers

procedure(foo_sub_abs), pointer :: p

or for a dummy arguments

subroutine bar(f)
  procedure(foo_sub_abs) :: f

and it means that the actual procedure to which p will point or which is passed as f conforms to the abstract interface.

Note that you are allowed to use some existing procedure instead of an abstract interface in both two former examples. It just needs to have explicit interface available in the scope (typically it is in the same module, or in a module which is used).


As far as I know (but see @IanH's comment below) the compiler is allowed to refuse your code:

  interface
    subroutine abstract_foo ( this, a, b )
      import :: abstract_t
      implicit none
      class(abstract_t), intent(in)  :: this
      real,              intent(in)  :: a
      real,              intent(out) :: b
    end subroutine
  end interface

because there exists no actual procedure named abstract_foo. Some compilers do not diagnose this, but they could.


Quite unrelated are generic interfaces. You can recognize them, because there is a name of a generic procedure after the word interface

  interface generic_sub
    procedure sub1

    subroutine sub2(...)
    end subroutine
  end interface

Here sub1 and sub2 both exist, sub1 is already known and has already explicit interface available, sub2 is external and looks as the interface specifies, and both are specific procedure of the generic generic_sub. This is quite a different usage.

You then call

call generic_sub(...)

and according to the arguments you pass, the compiler chooses which specific procedure is called, if it is sub1, or sub2.

I would stress that these can be split into sparate interface blocks with the same name declared at different locations. You can add a specific procedure into an existing generic procedure this way.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1