comp.lang.ada
 help / color / mirror / Atom feed
* Cumbersome Polymorphism
@ 1997-01-23  0:00 Richard Irvine
       [not found] ` <5c9put$48t@hetre.wanadoo.fr>
  1997-01-27  0:00 ` Norman H. Cohen
  0 siblings, 2 replies; 12+ messages in thread
From: Richard Irvine @ 1997-01-23  0:00 UTC (permalink / raw)



Here is a simple coding problem, some attempted and possible solutions,
and finally some moderately contentious conclusions.

I want to call a procedure of a package to store a value
in one of that package's internal variables and then have
another procedure which will do something with that internal
variable at a later time.
And in these OO times let's say that the value to be stored
can be polymorphic, i.e. its type could be any of the types
in a class of types.


--------------------
Attempted Solution 1
--------------------

To start with define the tagged types:


package Types is

  type Abstract_Type is abstract tagged null record;

  procedure Do_Something_To(A_Descendent : Abstract_Type);

  type Descendent1 is new Abstract_Type with null record;

  type Descendent2 is new Abstract_Type with null record;

end;


Here is a fairly natural first attempt at the package which will store
the value:


with Types;
use  Types;

package A_Package is

  procedure Store(A_Descendent : Abstract_Type'Class);

  procedure Do_Something_To_The_Stored_Descendent;

end;

package body A_Package is

  The_Stored_Descendent : Abstract_Type'Class;

  procedure Store(A_Descendent : Abstract_Type'Class)
  is
  begin
     The_Stored_Descendent := A_Descendent;
  end;

  procedure Do_Something_To_The_Stored_Descendent
  is
  begin
    Do_Something_To(The_Stored_Descendent);
  end;

end;


The compiler points out why the body of A_Package is not correct 
with the message

  initialization required in class-wide declaration

referring to The_Stored_Descendent : Abstract_Type'Class;

A variable declared to be of type Type'Class must be initialised
when it is declared.
In other words, one cannot simply say that a variable may contain
an instance of any of the types in a class.
(I suppose that the underlying problem is that since the sizes of
the types in the class are different, the compiler does not know
how much storage to set aside for the variable).

Furthermore, if I were to initialise the variable using an instance
of a member of the class of types I could only use the variable 
subsequently to store other instances of that member of the class,
in other words I could not use the variable polymorphically.

So clearly this is the wrong way to go about doing things in Ada.
The (a) right way  is to make use of access types which may access
instances of all of the types in the class, e.g.

----------
Solution 2
----------

package Types is

  type Abstract_Type is abstract tagged null record;

  procedure Do_Something_To(A_Descendent : Abstract_Type);

  type Access_Abstract_Type_Class is access Abstract_Type'Class;

  type Descendent1 is new Abstract_Type with null record;

  type Descendent2 is new Abstract_Type with null record;

end;

package body A_Package is

  Access_The_Stored_Descendent : Access_Abstract_Type_Class;

  procedure Store(A_Descendent : Abstract_Type'Class)
  is
  begin
     Access_The_Stored_Descendent := 
        new Abstract_Type'Class'(A_Descendent);
  end;

  procedure Do_Something_To_The_Stored_Descendent
  is
  begin
    Do_Something_To(Access_The_Stored_Descendent.all);
  end;

end;


This is a possible solution to the problem.
(In this case the compiler knows how much storage to set aside
for the access type, because this is independent of the size
of the type to which it points).
However, it seems that in order to make a very simple use of
polymorphism, I have been obliged to introduce access types.
And now by bringing in access types I have imported the problems
that go with them.
The above package does not free the storage which is allocated
in the Store operation.
In this case, I could simply introduce an explicit deallocation
in the Store operation, before allocating new storage.
However, as is well known, the problem with requiring the user
of a type to perform explicit deallocation is that he may forget
to do so somewhere in the code, resulting in a memory leak.

Of course, Ada provides a mechanism for automating deallocation,
using controlled types, so a better solution would make use of this:

----------
Solution 3
----------

with Ada.Finalization;
use  Ada.Finalization;

package Types is

  type Abstract_Type is abstract tagged private;

  procedure Do_Something_To(A_Descendent : Abstract_Type);

  type Access_Abstract_Type_Class is access Abstract_Type'Class;

  type Controlled_Access_Abstract_Type_Class is new Controlled with
    record
       The_Access_Abstract_Type_Class: Access_Abstract_Type_Class 
          := null;
    end record;

  type Descendent1 is new Abstract_Type with private;

  type Descendent2 is new Abstract_Type with private;

private

  type Abstract_Type is abstract tagged null record;

  procedure Finalize  
     ( Object : in out Controlled_Access_Abstract_Type_Class);

  type Descendent1 is new Abstract_Type with null record;

  type Descendent2 is new Abstract_Type with null record;

end;


with Ada.Tags;
use  Ada.Tags;
with Ada.Unchecked_Deallocation;
with Text_IO;
use  Text_IO;

package body Types is

  procedure Do_Something_To(A_Descendent : Abstract_Type)
  is
  begin
  -- write out the type (tag) of the descendent
     Text_IO.Put_Line("I am a " & Expanded_Name(A_Descendent'Tag));
  end;

  procedure Free
     is new Ada.Unchecked_Deallocation
                  (Object => Abstract_Type'Class,
                   Name   => Access_Abstract_Type_Class);

  procedure Finalize  
     (Object : in out Controlled_Access_Abstract_Type_Class)
  is
  begin
     Put_Line("Finalize " & Expanded_Name(Object'Tag));
     Free(Object.The_Access_Abstract_Type_Class);
  end;

end;

with Ada.Finalization;
use  Ada.Finalization;

package body A_Package is

  Controlled_Access_The_Stored_Descendent :
          Controlled_Access_Abstract_Type_Class;

  procedure Store(A_Descendent : Abstract_Type'Class)
  is
  begin
     Controlled_Access_The_Stored_Descendent :=
        Controlled_Access_Abstract_Type_Class'
           (Controlled with
            The_Access_Abstract_Type_Class 
               => new Abstract_Type'Class'(A_Descendent));
  end;

  procedure Do_Something_To_The_Stored_Descendent
  is
  begin
    Do_Something_To(Controlled_Access_The_Stored_Descendent
                    .The_Access_Abstract_Type_Class
                    .all);
  end;

end;

-----------
Conclusions
-----------

First of all, I look forward to improvements or corrections 
that anyone would like to suggest. 

Probably solution 3 is not the best or most straightforward possible,
but consider the effort involved in constructing this solution,
compared with the simplicity of the problem.

Before using Ada I used Smalltalk.
A Smalltalk variable can hold an instance of any class.
Of course, this is possible because a Smalltalk variable is just 
a pointer to dynamically allocated storage.
The important point though is that the pointers and the problems of 
allocation and deallocation are hidden from the Smalltalk programmer,
while the Ada programmer is obliged to be (painfully) aware of them 
much of the time.
(I know next to nothing about Java but believe that it has similar 
benefits, packages too).

I am left with the feeling that for object oriented progamming Ada 95 
is heavily cerebral, not to say cumbersome.
There are certainly many large, highly reliable or safety-critical 
applications where it should be the language of choice. 
And at the other end of the spectrum Smalltalk is probably preferable
for small rapid prototyping. 
For the enormous variety of applications in between I think that
the choice between Smalltalk, Java, Ada etc. still requires careful
consideration, despite the advent of Ada 95.




^ permalink raw reply	[flat|nested] 12+ messages in thread

end of thread, other threads:[~1997-01-29  0:00 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
1997-01-23  0:00 Cumbersome Polymorphism Richard Irvine
     [not found] ` <5c9put$48t@hetre.wanadoo.fr>
1997-01-25  0:00   ` James O'Connor
1997-01-25  0:00   ` Robert A Duff
1997-01-25  0:00     ` James O'Connor
1997-01-26  0:00       ` Robert Dewar
1997-01-26  0:00       ` Brian Rogoff
1997-01-27  0:00   ` Jon S Anthony
1997-01-29  0:00     ` Robert Dewar
1997-01-27  0:00 ` Norman H. Cohen
1997-01-28  0:00   ` Dave Gibson
1997-01-28  0:00   ` Jon S Anthony
1997-01-28  0:00   ` Richard Irvine

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