J3/98-218 Date: 01 November 1998 To: J3 From: Werner Schulz Subject: OOP in F2000: Polymorphic Objects In paper 98-217 I suggested how to handle polymorphic objects safely. In this paper I want to justify my approach, why it is different from the current F2000 draft and the advantages of my scheme. The very nature of polymorphic objects requires that they are implemented as (some form of) pointers limited by specifying a base class: ref(Point2D) :: PO_2 ref(Point3D) :: PO_3 class(Point2D) :: P2 class(Point3D) :: P3 class(Point4D) :: P4 PO_2 => P2 ! ok PO_2 => P3 ! ok PO_3 => P3 ! ok PO_3 => P4 ! ok PO_2 => PO_3 ! ok PO_3 => P2 ! illegal PO_3 => PO_2 ! illegal In general, a REF(base_class) polymorphic object can point to any object of the declared base_class or any instance of a subclass thereof. Nothing new here. This also implies that a polymorphic object P can only point to another polymorphic object Q if Q's base_class is the same or a subclass of P's base_class (PO_2 => PO_3). While all this is rather straightforward, the F2000 draft complicates matters by introducing non-pointer polymrophic objects. The extra adjective "non-pointer" already indicates that these objects are somewhat unusual. I argue that they are completely unnecessary and unsafe. Non-pointer polymorphic objects are unnecessary since a SELF construct (see 98-217) takes care of the only case where the current F2000 draft really needs them. No other language (except Ada95) uses non-pointer polymorphic objects. Indeed, many OOP languages (Java) have no objects but pointer-like polymorphic objects, not even monomorphic objects (which I consider a great weakness, Fortran's approach is much better and more efficient). Ada95 has non-pointer polymorphic objects (class-wide types) but that's only because Ada95 has a very special approach of its own for dealing with object-orientation which has no definite advantage. Non-pointer polymorphic objects are unsafe because certain operations can fail at run-time. Switching to Fortran 2000 draft syntax for the moment: CLASS(Point2D) :: P, Q P may contain a Point3D and Q a Point4D at run-time, but we don't anything else about P and Q at compile-time. Since P and Q are now non-pointer objects we must allow, for example, assignment: Q = P but for the above case this fails since Point4D = Point3D is impossible. Indeed, the whole idea of non-pointer polymorphic objects is full of these potential run-time failures. The only safe operations on polymorphic objects are: - pointer assignment (no ordinary assignment) - invoking procedures decalred in the base_class (except those with a LIKE-type dummy argument) - passing to other procedures as actual objects (if the dummy argument is also a polymorphic object) That's it. Nothing else is safe. But this is sufficient to operate with polymorphic objects. A careful analysis of other OOP languages will reveal that most allow these and only these operations. Some go further but the extra gain is a more than balanced by the potential of run-time errors. If one carefully examines the idea of polymorphic objects it will become clear that the very nature of them does pose some very stringent constraint on their use. It should also become clear that these constraints are very natural and any attempt to go further is nothing but a misunderstanding or plain greed. In my opinion it is more helpful to present Fortran users with a clear and logically consistent version of polymorphism than to attempt more and confuse. If it turns out that more is possible it can always be allowed at a later stage without sacrificing anything. However, going too far now makes it virtually impossible to remove the problem later on due to backward compatibility.