Search code examples
fortranpolymorphism

Can Fortran polymorphism work with allocatable arrays of different rank?


Can a Fortran subroutine use polymorphism to do something like this:

subroutine (inarr)

class(*) :: inarr

select type(inarr)

type is (real, allocatable(:))
   ! do something for 1-D array, maybe even allocate it
type is (real, allocatable(:,:))
   ! do something for 2-D array, maybe even allocate it
type is etc.

The syntax in the above is, of course, not going to work. But I figure that what I am wanting to do is clear. Any way to do it?


Solution

  • Polymorphism in Fortran is about types of entities: how an object may have different (dynamic) type at different points of execution. Polymorphism isn't about attributes: if an entity at one point in time has the SAVE, VOLATILE, POINTER or, yes, DIMENSION attribute it has it at every other time.1

    Within a procedure, some objects are allowed to assume part of their nature from another entity. For example2:

    subroutine sub(a, b, c, d, e)
      type(*) a        ! Assumes the type
      character(*) b   ! Assumes the length
      dimension c(*)   ! Assumes the size
      dimension d(:)   ! Assumes the shape
    end subroutine sub
    

    Although each of these arguments may "look different" each time sub is referenced, only a is polymorphic.

    In a SELECT TYPE construct only types, coupled with kind type parameters, can be used to discriminate cases. Attributes and length type parameters are not part of the selection process.

    There's something like the SELECT TYPE construct which works with the rank (number of dimensions) of an object: the SELECT RANK construct. Much like how a type may be assumed, the rank of a dummy argument may also be assumed:

    subroutine sub(x)
      dimension(..) x   ! Assumes the rank of the effective argument
    
      select rank (x)
      rank(0)       ! x is associated with a scalar
      rank(1)       ! x is associated with a rank-1 array which is not-assumed size
      rank(*)       ! x is associated with an assumed-size array
      rank default  ! x is associated with something else
    end subroutine
    

    This SELECT RANK ignores all other attributes, including whether x is allocatable.3

    The associating entity in a SELECT RANK construct has the same interesting attributes as the selector itself. Notably, if the assumed rank entity is allocatable the associating entity is allocatable.

    An assumed property can't be changed for that entity. An assumed type object, even though the thing is polymorphic, can't have its type changed until it assumed another type. An assumed type parameter won't change, an assumed shape or size won't change, and an assumed rank won't change.

    Equally, there's not much one can do with something with assumed type or rank. (Assuming type and rank are motivated mainly by the goal of interoperating with a language like C which doesn't so strongly care about types and ranks.) You aren't doing much with an assumed-rank object except using SELECT RANK, associating it with another assumed-rank dummy argument, or using in a handful of other functions.

    If you're dealing entirely with Fortran, you'll generally want to stick to generics: using type(*), dimension(..) :: arg may reduce the number of procedures you have to write, but it doesn't reduce duplication.


    1 An entity may, for example, have the POINTER attribute, but be associated with something without the POINTER attribute. Still, the first entity has the POINTER attribute, and the second hasn't.

    2 A character function result can even assume its length in a way best avoided.

    3 Except for noting that an allocatable x will never be associated with an assumed-size array.