comp.lang.ada
 help / color / mirror / Atom feed
From: Nick Roberts <nick.roberts@acm.org>
Subject: Re: Abstract Operations On A Tagged Record
Date: Sat, 30 Oct 2004 17:07:41 +0100
Date: 2004-10-30T17:07:41+01:00	[thread overview]
Message-ID: <2uhsidF2ahq9fU1@uni-berlin.de> (raw)
In-Reply-To: <FjMgd.12476$5i5.2958@newsread2.news.atl.earthlink.net>

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



  parent reply	other threads:[~2004-10-30 16:07 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2004-10-30 13:04 Abstract Operations On A Tagged Record Marin David Condic
2004-10-30 14:34 ` Dmitry A. Kazakov
2004-10-30 16:07 ` Nick Roberts [this message]
2004-11-01 12:58   ` Marin David Condic
2004-11-01 18:09     ` Nick Roberts
2004-11-01 22:27       ` Marin David Condic
2004-11-06 13:17       ` Marin David Condic
replies disabled

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox