Intel® Fortran Compiler
Build applications that can scale for the future with optimized code designed for Intel® Xeon® and compatible processors.

Trying to understand type extension and type bound procedures

Svein-Atle_Engeseth
360 Views

I have a program where these types are defined:

       TYPE :: VECTOR_2D

        !
        PRIVATE
        !
        REAL (WP) :: X = 0.0_WP, Y = 0.0_WP
        !
        CONTAINS
        !
        PROCEDURE, PRIVATE :: GET_2D
        !
        PROCEDURE, PUBLIC :: WRITE => WRITE_2D
        !
        PROCEDURE, PRIVATE :: EQUAL_TO_2D
        !
  !      PROCEDURE, PUBLIC :: SET => SET_2D ! Uncomment this line and comment the next two and the program doesn't work, why?
        !
        PROCEDURE, PRIVATE :: SET_2D
        !
        GENERIC, PUBLIC :: SET => SET_2D
        !
      END TYPE VECTOR_2D
      !
      TYPE, EXTENDS(VECTOR_2D) :: VECTOR_3D
        !
        PRIVATE
        !
        REAL (WP) :: Z = 0.0_WP
        !
        CONTAINS
        !
        PROCEDURE, PRIVATE :: GET_3D
        !
        PROCEDURE, PUBLIC :: WRITE => WRITE_3D
        !
        PROCEDURE, PRIVATE :: EQUAL_TO_3D
        !
  !      PROCEDURE, PUBLIC :: SET => SET_3D ! Uncomment this line and comment the next two and the program doesn't work, why?
        !
        PROCEDURE, PRIVATE :: SET_3D
        !
        GENERIC, PUBLIC :: SET => SET_3D
        !
      END TYPE VECTOR_3D

When I compile this program this warning appears:

C:\F90\PGM\F_2003_TEST11.F90(66): warning #8449: The type/rank/keyword signature for this specific procedure matches another specific procedure that shares the same generic binding name.   [SET_2D]

If I uncomment  and comment the lines as described in the code snippet the program does not compile. I get these errors:

Severity    Code    Description    Project    File    Line    Suppression State
Error        error #8383: The dummy arguments of an overriding and overridden binding that correspond by position must have the same characteristics, except for the type of the passed object dummy arguments.   [SET]        C:\F90\PGM\F_2003_TEST11.F90    52    

In the documentation I find this example:

TYPE MY_TYPE2
...   
CONTAINS
  PROCEDURE :: MYPROC => MYPROC1
  PROCEDURE,PASS(C) :: UPROC => UPROC1
  GENERIC :: OPERATOR(+) => MYPROC, UPROC
END TYPE MY_TYPE2
...
TYPE,EXTENDS(MY_TYPE2) :: MY_TYPE3
...
CONTAINS
  PROCEDURE :: MYPROC => MYPROC2
  PROCEDURE,PASS(C) :: UPROC => UPROC2
END TYPE MY_TYPE3

In the first version the warning is that the procedure arguments have the same TYPE/Rank and in the second the error is that they are different. Which way should it be?

Regards,

 

 

 

 

0 Kudos
3 Replies
IanH
Honored Contributor II
360 Views

As shown in the post, each type is attempting to extend the generic name `Set` by adding a specific binding to it.  This is often referred to as overloading - providing multiple meanings for the same name at the same time.  When you overload a generic identifier, the specific bindings and procedures behind the generic must be distinguishable, so that, at most, there will be one specific procedure or binding that matches a call (elemental procedures aside).  Argument disambiguation is done on the basis of type, kind and rank - the second argument to your SET_2D and SET_3D procedures have the same type, kind and rank, so they are not distinguishable.

With the lines commented out (giving what is in the attachment), the VECTOR_3D type is attempting to replace the definition of the `Set` specific binding inherited from the VECTOR_2D type, with its own definition.  This is often referred to as overriding.  When you override a specific binding, the characteristics of the overridden and overriding procedure must match, type of the passed object aside, to permit direct substitution of the specific procedure that gets invoked by a reference through the binding, based on the dynamic type of the object with the binding.  The characteristics of an argument include any array specification - the second argument to your SET_2D and SET_3D procedures have different explicit shape array specifications.  If the relevant dummy arguments in the two procedures were assumed shape `(:)`, then the characteristics would match.

Note that in the example fragment from the documentation, there is no generic binding declaration in the second type - it is just overloading the two specific bindings.  The OPERATOR(+) generic is inherited from MY_TYPE2, but no further bindings are added to it.

The SET_2D and SET_3D procedures are constructor like in nature.  My experience is that generally such procedures should not be bindings at all.

0 Kudos
Svein-Atle_Engeseth
360 Views

Hi Ian,

Thanks for your reply. Thanks for clarifying the differences between overriding and overloading, I have to do some work on that.
Why shouldn't constructors be bound routines? I thought that was canon in the OOP camp.
A question?
Having declared the types
TYPE :: VECTOR_2D       and       TYPE, EXTENDS(VECTOR_2D) :: VECTOR_3D,
using these in two subroutines
sub A(X) and X declared as CLASS(VECTOR_2D) and another sub B(Y) and Y declared as CLASS(VECTOR_3D) ,
do X and Y have the same type?

Regards,

 

0 Kudos
IanH
Honored Contributor II
360 Views

I am not the one to be making definitive statements about what OOP is or isn't (my background is in breaking rocks, not computer science), but in order to construct something, you generally need to provide information that is specific to the type of thing to be constructed - different types, different information.  Type bound specific procedures are a mechanism for doing something different with the same information.

For a simple example, to construct a VECTOR_2D object - you need two real values.  To construct a VECTOR_3D object you need three values.  Different types - different information. 

(This might not be the best example - because the language permits multiple values of the same type to be packaged into a single thing that is an array, but your different types have different requirements on that array, so the concept is still lurking there.)

To contrast, consider a method that was "to set the first two components of a vector".  The information required for that operation is always two values.  It is equally applicable to objects of type VECTOR_2D and VECTOR_3D, but in the latter case, there would be behaviour specific to VECTOR_3D around what happens with the Z component.

In terms of your question about "the same type", the answer is "No - they are not the same type".

When you override a specific binding, the dummy arguments in the procedure nominated by the overriding binding must be the same type, bar any passed argument.  The requirement for the non-passed dummy arguments to be of the same type (and all the other characteristics) reflects the "same information" concept discussed above.

When you extend a generic, the rules around disambiguation of arguments are written in terms of type compatibility.  If VECTOR_3D is an extension of VECTOR_2D, then a thing declared CLASS(VECTOR_2D) is type compatible with an object of declared type VECTOR_3D.  But a thing declared CLASS(VECTOR_3D) is not type compatible with an object of declared type VECTOR_2D.  There is a "direction" to type compatibility that needs to be considered when invoking a procedure - but when you are extending a generic, the requirement for a dummy argument to be distiguishable is that neither of the dummy arguments being considered be compatible with the other (both directions must be incompatible).

I am only guessing wildly at what you are trying to do (perhaps you could elaborate, otherwise there is a very real risk that I misunderstand and derail your efforts), but as a general guideline (not a rule), when you have a type extension hierarchy, object that are of extension type should be directly substitutable for polymorphic objects of the parent type.  There will be some operations that are common to 2D vectors and 3D vectors (and probably any "dimension" of vector - perhaps vector is a useful ABSTRACT concept to start with...), but others will be specific to a particular "dimension".  Keeping that principle in mind can help when designing types. 

(For completeness, and again depending on what you want to do, your concept of a the "dimension" of a vector could also be parameterised out.)

0 Kudos
Reply