Type-bound procedure with array

I have a question about invoking a type bound procedure on an array. I thought I knew how to do this, but what I’m trying isn’t working. Look at this code:

   implicit none
   public :: tbp_t
   type tbp_t
      integer :: i
   contains
      procedure :: p_0 => print_0
      !procedure :: p_1 => print_1
   end type tbp_t
contains
   subroutine print_0( this, s )
      class(tbp_t), intent(in) :: this
      character(*), intent(in) :: s
      print '(a,1x,a,1x,i0)', 'print_0:', s, this%i
      return
   end subroutine print_0
   subroutine print_1( this, s )
      class(tbp_t), intent(in) :: this(:)
      character(*), intent(in) :: s
      integer :: p
      print '(a)', 'print_1:'
      do p = 1, size(this)
         call this(p)%p_0( s )
      enddo
      return
   end subroutine print_1
end module tbp_mod
program xxx
   use tbp_mod
   implicit none
   type(tbp_t) :: a, b(3)
   a%i = 42
   b%i = [1,2,3]
   call print_0( a, 'standard scalar' )
   call a%p_0( 'tbp scalar' )
   call print_1( b, 'standard array' )
   !call b%p_1( 'tbp array' )
end program xxx

This small program prints a derived type in the standard way, and then prints the same thing through the type bound procedure. Then the program prints an array in the standard way. But I don’t see how to print the array with a type bound procedure. The two lines that are commented out in the above program are the ones that do not work. I cannot see how to bind the procedure (print_1() in this code) to the derived type.

Anyone know the right incantation?

Seems like you can achive what you want if p_0 is declared elemental (or in your case impure elemental because of the print)

module tbp_mod
implicit none
   public :: tbp_t
   type tbp_t
      integer :: i
   contains
      procedure :: p_0 => print_0
   end type tbp_t
contains
   impure elemental subroutine print_0( this, s )
      class(tbp_t), intent(in) :: this
      character(*), intent(in) :: s
      print '(a,1x,a,1x,i0)', 'print_0:', s, this%i
      return
   end subroutine print_0
end module tbp_mod
program xxx
   use tbp_mod
   implicit none
   type(tbp_t) :: a, b(3)
   a%i = 42
   b%i = [1,2,3]
   call print_0( a, 'standard scalar' )
   call a%p_0( 'tbp scalar' )
   call b%p_0( ['tbp array', 'tbp array', 'tbp array'] )
end program xxx

An alternative could be to use a signature like:

procedure, nopass :: p_1 => print _1

subroutine print_1( array )

etc.

if an elemental procedure would not be appropriate (I can imagine that you might want to include the index or something like that)

This seems the most concise solution, and you can just pass a scalar CHARACTER that would be used for all elements. If you want special handling for arrays (you might want to elide elements), then something like this:

module tbp_mod
implicit none
   public :: tbp_t
   type tbp_t
      integer :: i
   contains
      procedure :: p_0 => print_0
      procedure :: p_1 => print_1
   end type tbp_t
contains
   impure elemental subroutine print_0( this, s )
      class(tbp_t), intent(in) :: this
      character(*), intent(in) :: s
      print '(a,1x,a,1x,i0)', 'print_0:', s, this%i
      return
    end subroutine print_0
    subroutine print_1(this, arr, s)
      class(tbp_t), intent(in) :: this, arr(:)
      character(*), intent(in) :: s
      print '(a,1x,a,1x,*(i0,:,","))', 'print_1:', s, arr%i
    end subroutine print_1
end module tbp_mod
program xxx
   use tbp_mod
   implicit none
   type(tbp_t) :: a, b(3)
   integer :: m
   a%i = 42
   b%i = [1,2,3]
   call print_0( a, 'standard scalar' )
   call print_0( b, 'standard array' )

   call a%p_0( 'tbp scalar' )
   call b%p_0( 'tbp array' )

   call b(1)%p_1(b, 'alt')
   call b(1)%p_1([(b(1),m=1,0)], 'alt')
end program xxx

Thanks for the suggestions. The impure elemental combination solves my problem.

However, it does seem odd that a more general procedure that takes an array argument cannot be a type bound procedure. My original plan was to write a scalar procedure, and an array procedure, and then bind them both to the derived type and make them generic with something like

generic :: sub => sub_scalar, sub_array

in the type definition. But if you can’t bind sub_array in the first place, then none of that works.

Not as elegant as intended, but this might be a workaround:

module tbp_mod
   implicit none
   public :: tbp_t
   type tbp_t
      integer :: i
   contains
      procedure :: p_0 => print_0
      procedure :: p_1 => print_1
      generic :: print => p_0, p_1
   end type tbp_t
contains
   subroutine print_0( this, s )
      class(tbp_t), intent(in) :: this
      character(*), intent(in) :: s
      print '(a,1x,a,1x,i0)', 'print_0:', s, this%i
      return
   end subroutine print_0
   subroutine print_1( this, array, s )
      class(tbp_t), intent(in) :: this
      class(tbp_t), intent(in) :: array(:)
      character(*), intent(in) :: s
      integer :: p
      print '(a)', 'print_1:'
      do p = 1, size(array)
         call array(p)%p_0( s )
      enddo
      return
   end subroutine print_1
end module tbp_mod
program xxx
   use tbp_mod
   implicit none
   type(tbp_t) :: a, b(3)
   a%i = 42
   b%i = [1,2,3]
   call a%print( 'standard scalar' )
   call b(1)%print( b, 'standard array' )
end program xxx

It all comes down to the passed-object (i.e., the hidden this, self or receiver in other languages). The standard explicitly says:

C765
The passed-object dummy argument shall be a scalar, nonpointer, nonallocatable dummy data object
with the same declared type as the type being defined; all of its length type parameters shall be assumed;
it shall be polymorphic (7.3.2.3) if and only if the type being defined is extensible (7.5.7). It shall not
have the VALUE attribute.

If you think in terms of [obj msg] (i.e., a message being dynamically dispatched to eventually be handled by an object), then it makes more sense for…

  • the object to be scalar: one message handled by one object,
    [this p_0(s)].

  • the procedure to be elemental:

    • one message handled by one object,
      [this p_0(s)].

    • multiple messages, each handled by a distinct object,
      [this(1:5) p_0(s(1:5))].

    • one message, handled simultaneously by each element in an array of objects,
      [this(1:5) p_0(spread(s,1,5))].

The option not covered (by OOP), and which is probably the one you want, is having an array of objects to somehow handle a single non-elemental message —in my example, it’s not clear which of the 5 elements of obj(:) would receive the message.

I think your explanation is probably correct, and it makes sense.

With normal procedure calls, you can have a subprogram that takes a scalar argument, and you can have another subprogram that takes an array argument, and you can define a generic interface and reference that interface with either a scalar argument or an array argument. The compiler looks at the argument list and finds the appropriate match at compile time. I thought that one could do the same thing with type bound procedures, including the compile time resolution of the generic interface.