From mboxrd@z Thu Jan 1 00:00:00 1970 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on polar.synack.me X-Spam-Level: X-Spam-Status: No, score=-1.9 required=5.0 tests=BAYES_00 autolearn=ham autolearn_force=no version=3.4.4 X-Google-Thread: 103376,6129ccd596d4814d X-Google-Attributes: gid103376,public X-Google-Language: ENGLISH,ASCII-7-bit Path: g2news1.google.com!news1.google.com!news.glorb.com!fu-berlin.de!uni-berlin.de!not-for-mail From: Nick Roberts Newsgroups: comp.lang.ada Subject: Re: Abstract Operations On A Tagged Record Date: Sat, 30 Oct 2004 17:07:41 +0100 Message-ID: <2uhsidF2ahq9fU1@uni-berlin.de> References: Mime-Version: 1.0 Content-Type: text/plain; charset=us-ascii; format=flowed Content-Transfer-Encoding: 7bit X-Trace: news.uni-berlin.de KYLPPYnNT0UdP4mgENrDRwRd473CHOLZWdSUQBoistFtfJJTs= User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7.2) Gecko/20040803 X-Accept-Language: en-us, en In-Reply-To: Xref: g2news1.google.com comp.lang.ada:5916 Date: 2004-10-30T17:07:41+01:00 List-Id: Marin David Condic wrote: > Can someone clarify my understanding (perhaps again) on abstract > operations and the implications as a class heierarchy is being > constructed? Specifically, what I want is something like this: > > A base class has an abstract operation, but I may not yet know the data > types of the parameters - or possibly even the number of parameters. > ... > The base class wants to express the basic necessity of "Some_Op" but it > really doesn't know if it will have 1, 2 or N parameters or of what > type. My understanding is that if I express that as an abstract > operation, when I get to the child class, I'll have to supply a real > operation of the same name and same parameter profile. Correct. > (Or can I use a > different parameter profile & satisfy the need for a concrete > implementation of the abstract op? No. > My recollection is the compiler > whines about it or creates an overloading. Is this correct? Correct. (The compiler whines ;-) > I realize I can make another procedure "Some_Op" with a different > parameter list and that overloading will take care of me, but then I > didn't really need the abstract procedure in the first place, did I? It > becomes unnecessary baggage that you have to fill in just because its > there, but you have no intention of using it. Worse than that, you have no dynamic dispatching, which is the whole point of using a tagged type in the first place. Disaster. > Do I understand this correctly? Is there away around it? The solution is simple, Marin. You use /two/ type hierarchies, one to represent the primary objects, and a secondary one to represent the detailed parameters of the primitive operation in question. For example, suppose we have a primary hierarchy of shapes, and we want a primitive operation transforming a shape according to a set of parameters that depends on the shape (and the kind of transformation): type Root_Shape_Type is abstract tagged private; type Transformation_Spec is abstract tagged private; function Transform (Shape: in Root_Shape_Type; Spec: in Transformation_Spec'Class) return Root_Shape_Type'Class is abstract; Transformation_Error: exception; Note carefully that Transform is a primitive operation of the primary type Root_Shape_Type. The Spec parameter is of a class wide type, and so is the result (since a transformation might produce a different kind of shape). We could then declare some transformations, such as: type Rotation is new Transformation_Spec with record Angle: Radians; end record; type Translation is new Transformation_Spec with record Offset: Surface_Offset; end record; and we might declare a polygon, say, as follows: type Polygonal_Shape is new Root_Shape_Type with private; function Points (Shape: in Polygonal_Shape) return Point_Vector; ... -- other polygon operations function Transform (Shape: in Polygonal_Shape; Spec: in Transformation_Spec'Class) return Root_Shape_Type'Class; The body of the Transform function for polygons would have to test the actual type of the Spec parameter to determine which tranformation to perform: function Transform (Shape: in Polygonal_Shape; Spec: in Transformation_Spec'Class) return Root_Shape_Type'Class is begin if Spec in Rotation then declare Rot_Spec: constant Rotation := Rotation(Spec); Center: constant Point := Centerpoint(Shape); Old_Points: constant Point_Vector := Points(Shape); New_Points: Point_Triple(Old_Points'Range); begin for P in Old_Points'Range loop New_Points(P) := Rotate( Old_Points(P), Center, Rot_Spec.Angle ); end loop; return To_Polygon(New_Points); end; elsif Spec in Translation then ... else raise Transformation_Error; -- unrecognized transformation end if; end Transform; I think it is interesting to note that some languages, such as Cecil, provide a way to select an operation's implementation (body) on two or more parameters, rather than just one. Cecil is a very interesting language (but not yet mature enough for real use, I think). Nevertheless, Ada's lowly single selection still permits new shapes to be added to a program without having to worry about how this affects other parts of the program (in general), and this could be very useful. Note also that you might possibly wish to swap the roles of shape and transformation, so that Transform becomes a primitive operation of Transformation_Spec (instead of Root_Shape_Type), and bodies of the function have to test for different shapes. This would allow you to add new transformations in a very independent manner. HTH -- Nick Roberts