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

* Re: Cumbersome Polymorphism
       [not found] ` <5c9put$48t@hetre.wanadoo.fr>
@ 1997-01-25  0:00   ` Robert A Duff
  1997-01-25  0:00     ` James O'Connor
  1997-01-25  0:00   ` James O'Connor
  1997-01-27  0:00   ` Jon S Anthony
  2 siblings, 1 reply; 12+ messages in thread
From: Robert A Duff @ 1997-01-25  0:00 UTC (permalink / raw)



In article <5c9put$48t@hetre.wanadoo.fr>,
J-P. Rosen <Rosen.Adalog@wanadoo.fr> wrote:
>Richard Irvine <irv@eurocontrol.fr> wrote:
>>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.

The problems of allocation and deallocation are hidden from the
Smalltalk programmer (and from the Ada programmer, if you can find a
garbage-collected implementation of Ada, which isn't easy).  But the
pointers are not hidden in either language: The fundamental thing that
makes a pointer a pointer is that two pointers can point at the same
thing.  This is what makes pointers useful.  And this happens in both
languages, despite the fact that dereferencing the pointer is always
implicit in Smalltalk.  In Ada, you are forced to use a pointer if you
have something that changes size, and this is indeed unfortunate from
the functionality point of view.  In Smalltalk, you are forced to use a
pointer ALWAYS, and IMHO that's even more unfortunate.

Note that in Ada, if you have to use a pointer for low-level
implemenation reasons, like the fact that the thing changes size, you
can hide this fact from clients.  You can't do that in Smalltalk, if I
remember correctly, since assignment in Smalltalk is always allowed, and
always means "copy the pointer" -- there's no simple way of preventing
the client from evilly making aliases.

- Bob




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

* Re: Cumbersome Polymorphism
       [not found] ` <5c9put$48t@hetre.wanadoo.fr>
  1997-01-25  0:00   ` Robert A Duff
@ 1997-01-25  0:00   ` James O'Connor
  1997-01-27  0:00   ` Jon S Anthony
  2 siblings, 0 replies; 12+ messages in thread
From: James O'Connor @ 1997-01-25  0:00 UTC (permalink / raw)
  To: Rosen.Adalog


J-P. Rosen wrote:

[much deleted] 
> >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.
> You said it: the smalltalk solution is the same as Ada. The only
> difference is the presence of absence of a garbage collector. Well,
> this is an other thread of discussion...
> 
> >I am left with the feeling that for object oriented progamming Ada 95
> >is heavily cerebral, not to say cumbersome.
> A bit of quick conclusion...

Depends on your point of view.  If your background is Ada83 or C++ or
some other language han maybe Ada95 looks great.  If you backgropund is
Smalltalk than Ada95 feels very cumbersome.  It's a matter of
perspective.

> 
> >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.
> I agree with that

I wouldn't :)  Most commercial Smalltalk environments are very large and
as such, are not condusive to making 'small' prototypes.  Rapid
prototyping and RAD, yes.  Small, no.   Most Smalltalk environments are
actually designed for building large scale (distributed) systems.  Using
something like VisualAge or VisualWorks for small, quick, knock-off
protoypes is a waste of money and potential.


> +------------------------------------o-------------------------------------+
> |   J-P. Rosen                       |    Rosen.Adalog@wanadoo.fr          |
> |   ADALOG - 27 avenue de Verdun     |    Tel: +33 1 46 45 51 12           |
> |   92170 Vanves - FRANCE            |    Fax: +33 1 46 45 52 49           |
> +------------------------------------o-------------------------------------+

-- 
James O'Connor 
--------------------------------------
joconnor@jmpstart.com
http://www.jmpstart.com
--------------------------------------




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

* Re: Cumbersome Polymorphism
  1997-01-25  0:00   ` Robert A Duff
@ 1997-01-25  0:00     ` James O'Connor
  1997-01-26  0:00       ` Brian Rogoff
  1997-01-26  0:00       ` Robert Dewar
  0 siblings, 2 replies; 12+ messages in thread
From: James O'Connor @ 1997-01-25  0:00 UTC (permalink / raw)
  To: Robert A Duff


Robert A Duff wrote:
> 
> In article <5c9put$48t@hetre.wanadoo.fr>,
> J-P. Rosen <Rosen.Adalog@wanadoo.fr> wrote:
> >Richard Irvine <irv@eurocontrol.fr> wrote:
> >>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.
> 
> The problems of allocation and deallocation are hidden from the
> Smalltalk programmer (and from the Ada programmer, if you can find a
> garbage-collected implementation of Ada, which isn't easy).  But the
> pointers are not hidden in either language: The fundamental thing that
> makes a pointer a pointer is that two pointers can point at the same
> thing.  This is what makes pointers useful.  And this happens in both
> languages, despite the fact that dereferencing the pointer is always
> implicit in Smalltalk.  

That's actually a big difference in approach (IMHO).  While it's true
that pointers are not hidden in Smalltalk, they are also not viewed as
such.  Since every variable is really a pointer and every pointer is
automatically derefrenced, you stop thinking in terms of pointers and
just end up think in terms of the objects they point at.  There is no
'non-pointer' syntax to deal with.


> In Ada, you are forced to use a pointer if you
> have something that changes size, and this is indeed unfortunate from
> the functionality point of view.  In Smalltalk, you are forced to use a
> pointer ALWAYS, and IMHO that's even more unfortunate.

I'm curious, why do you consider that unfortunate?  It certainly makes
the sytanx very clean and alleviates a lot of headaches from
developers.  Like I said, you stop thinking in terms of pointers.

> 
> Note that in Ada, if you have to use a pointer for low-level
> implemenation reasons, like the fact that the thing changes size, you
> can hide this fact from clients.  You can't do that in Smalltalk, if I
> remember correctly, since assignment in Smalltalk is always allowed, and
> always means "copy the pointer" -- there's no simple way of preventing
> the client from evilly making aliases.

How do you evilly (sp?) make an alias?  Sounds like Frankenstein. 
Frankencode? If you have

| a b c| 
a := 'A String'
b := 47.
c := a.
c := 100.


If you evaluate a, it is still 'A String', but C is now 100.  Also there
are some limits on when you can do assignments.  For example, you cannot
directly assign to a method parameter

someMethod: anObject.

	anObject := 5.

Is illegal.
As is trying to directly change an instance variable

changePoint: aPoint

	aPoint x := 100.

As is trying to copy and switch.

changePoint: aPoint.

	myX := aPoint x.
	myX := 100.

The orignal aPoint will remain unchanged.
You still have to honor the public interface of the object you are
dealing with.
If you want to be 'evil' you can use #become: to switch object
identities, but the results of #become: are implementation dependent.
You can also do...

| anObject x |

x := anObject getCollection.
x add: SomeUglyClass new.

But that is considered bad form.  Usually your collections are kept
private and you provide an add: method for them in your own extrenbal
interface in which you can reject bad input.

Even though Smalltalk is dynamically typed and is all pointers.  It's
pretty hard to do something really stupid and pretty easy to protect
against it.  It's not impossible, though.  If you really try you can be
malicious, but then it's your own program that blows up.

> 
> - Bob

-- 
James O'Connor 
--------------------------------------
joconnor@jmpstart.com
http://www.jmpstart.com
--------------------------------------




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

* Re: Cumbersome Polymorphism
  1997-01-25  0:00     ` James O'Connor
  1997-01-26  0:00       ` Brian Rogoff
@ 1997-01-26  0:00       ` Robert Dewar
  1 sibling, 0 replies; 12+ messages in thread
From: Robert Dewar @ 1997-01-26  0:00 UTC (permalink / raw)



JPR wrote

> The problems of allocation and deallocation are hidden from the
> Smalltalk programmer (and from the Ada programmer, if you can find a
> garbage-collected implementation of Ada, which isn't easy).  But the


It is perfectly possible to hide allocation and deallocatoin from an
Ada programmer -- just look at Ada.Unbounded.Strings as an example.
Note that this also shows that variable length items can be handled
in a completely transparent manner.





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

* Re: Cumbersome Polymorphism
  1997-01-25  0:00     ` James O'Connor
@ 1997-01-26  0:00       ` Brian Rogoff
  1997-01-26  0:00       ` Robert Dewar
  1 sibling, 0 replies; 12+ messages in thread
From: Brian Rogoff @ 1997-01-26  0:00 UTC (permalink / raw)



On Sat, 25 Jan 1997, James O'Connor wrote:
> Robert A Duff wrote:
> > The problems of allocation and deallocation are hidden from the
> > Smalltalk programmer (and from the Ada programmer, if you can find a
> > garbage-collected implementation of Ada, which isn't easy).  But the
> > pointers are not hidden in either language: The fundamental thing that
> > makes a pointer a pointer is that two pointers can point at the same
> > thing.  This is what makes pointers useful.  And this happens in both
> > languages, despite the fact that dereferencing the pointer is always
> > implicit in Smalltalk.  
> 
> That's actually a big difference in approach (IMHO).  While it's true
> that pointers are not hidden in Smalltalk, they are also not viewed as
> such.  Since every variable is really a pointer and every pointer is
> automatically derefrenced, you stop thinking in terms of pointers and
> just end up think in terms of the objects they point at.  There is no
> 'non-pointer' syntax to deal with.

	There are many applications where the perceived performance
penalties of this approach are far too great. 

> > In Ada, you are forced to use a pointer if you
> > have something that changes size, and this is indeed unfortunate from
> > the functionality point of view.  In Smalltalk, you are forced to use a
> > pointer ALWAYS, and IMHO that's even more unfortunate.
> 
> I'm curious, why do you consider that unfortunate?  It certainly makes
> the sytanx very clean and alleviates a lot of headaches from
> developers.  Like I said, you stop thinking in terms of pointers.

	I won't presume to speak for Robert Duff, but as I said above,
there are many applications, particularly those of a numeric nature, where 
performance constraints force one to think in terms of values. Would you 
write a polymorphic matrix class in Smalltalk, and expect it to have
similar performance characteristics to a generic matrix type in Ada,
Eiffel (using expanded classes for the matrix elements), or (gak!) C++? 
Perhaps you think such applications are but a tiny fraction of all
applications, but I would disagree. Numerics and signal processing codes 
are found in lots of software.

	Perhaps someone will now mention the excellent Stalin compiler for
the dynamic language Scheme, which ameliorates this problem by using
agressive type inferencing. I'll reply that while I find this work very
promising, it is still research, it adds considerably to compile time, and 
hasn't been used with really gigantic codes.  

	IMO, the Dylan language approach of allowing optional type
information to be added to a dynamic "all objects like Smalltalk" language
designed from the start to be optimized for performance, seems like a 
good idea. We'll have to wait until real compilers are available to see 
how good it works in practice.

	None of the above is meant to imply that I think Smalltalk is not 
an excellent programming language (languages, really ;-) for a large range 
of applications. I would *much* rather use ST than Visual Basic or Tcl as 
high level glue, and use Ada 95 for the high performance compute engine. 
Alas, current industry practice seems to favor VB and C++.

-- Brian






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

* Re: Cumbersome Polymorphism
  1997-01-23  0:00 Cumbersome Polymorphism Richard Irvine
       [not found] ` <5c9put$48t@hetre.wanadoo.fr>
@ 1997-01-27  0:00 ` Norman H. Cohen
  1997-01-28  0:00   ` Jon S Anthony
                     ` (2 more replies)
  1 sibling, 3 replies; 12+ messages in thread
From: Norman H. Cohen @ 1997-01-27  0:00 UTC (permalink / raw)



Richard Irvine wrote:

> ----------
> 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.
...
> However, it seems that in order to make a very simple use of
> polymorphism, I have been obliged to introduce access types.

Yes, this is generally the case in Ada 95.

> And now by bringing in access types I have imported the problems
> that go with them.

You are using an access type in a very restricted and disciplined way. 
I'm not sure what you mean by "the problems that go with them", but the
fact that undisciplined use of access types may cause problems is
irrelevant.  Your solution does not involve any such problems.

> 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.

That is precisely the right approach:

   procedure Store(A_Descendent : Abstract_Type'Class) is
      procedure Free is
         new Ada.Unchecked_Deallocation
           (Abstract_Type'Class, Access_Abstract_Type_Class);
   begin
      Free (Access_The_Stored_Descendent);
         -- A no-op if Access_The_Stored_Descedent is
         --    already null.
      Access_The_Stored_Descendent :=
         new Abstract_Type'Class'(A_Descendent);
   end;
 

> 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.

I don't understand the problem.  The need for the deallocation operation
is encapsulated in the package.  The writer of the package ensures that
there is never more than one object allocated.  The user of the type
never has to know about it.  Indeed, the declaration of your access type
ought to be moved out of the package declaration and into the package
body.
 
> Of course, Ada provides a mechanism for automating deallocation,
> using controlled types, so a better solution would make use of this:

The use of controlled types is not warranted in this situation.

-- 
Norman H. Cohen
mailto:ncohen@watson.ibm.com
http://www.research.ibm.com/people/n/ncohen




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

* Re: Cumbersome Polymorphism
       [not found] ` <5c9put$48t@hetre.wanadoo.fr>
  1997-01-25  0:00   ` Robert A Duff
  1997-01-25  0:00   ` James O'Connor
@ 1997-01-27  0:00   ` Jon S Anthony
  1997-01-29  0:00     ` Robert Dewar
  2 siblings, 1 reply; 12+ messages in thread
From: Jon S Anthony @ 1997-01-27  0:00 UTC (permalink / raw)



In article <dewar.854255869@merv> dewar@merv.cs.nyu.edu (Robert Dewar) writes:

> Xref: alexandria.organon.com comp.lang.ada:54299
> Path: alexandria.organon.com!uunet!in3.uu.net!144.212.100.12!news.mathworks.com!enews.sgi.com!newshub1.home.com!news.home.com!ames!purdue!oitnews.harvard.edu!cmcl2.nyu.edu!news.nyu.edu!merv!dewar
> From: dewar@merv.cs.nyu.edu (Robert Dewar)
> Newsgroups: comp.lang.ada
> Date: 26 Jan 1997 00:18:53 -0500
> Organization: New York University
> Lines: 12
> References: <32E7ABE8.3BF3@eurocontrol.fr> <5c9put$48t@hetre.wanadoo.fr> <E4KKqz.CJM@world.std.com> <32EAA1A0.7C43@jmpstart.com>
> NNTP-Posting-Host: merv.cs.nyu.edu
> X-Newsreader: NN version 6.5.0 (NOV)
> 
> JPR wrote
> 
> > The problems of allocation and deallocation are hidden from the
> > Smalltalk programmer (and from the Ada programmer, if you can find a
> > garbage-collected implementation of Ada, which isn't easy).  But the
> 
> 
> It is perfectly possible to hide allocation and deallocatoin from an
> Ada programmer -- just look at Ada.Unbounded.Strings as an example.
> Note that this also shows that variable length items can be handled
> in a completely transparent manner.

You forgot to add "and sllllooooowwwwwww" to the end of your last
sentence.  Unless you mean that Unbound_Strings should have a GC'd
implementation.  In which case you would simply be agreeing with Bob.

/Jon
-- 
Jon Anthony
Organon Motives, Inc.
Belmont, MA 02178
617.484.3383
jsa@organon.com





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

* Re: Cumbersome Polymorphism
  1997-01-27  0:00 ` Norman H. Cohen
  1997-01-28  0:00   ` Jon S Anthony
@ 1997-01-28  0:00   ` Dave Gibson
  1997-01-28  0:00   ` Richard Irvine
  2 siblings, 0 replies; 12+ messages in thread
From: Dave Gibson @ 1997-01-28  0:00 UTC (permalink / raw)



> > However, it seems that in order to make a very simple use of
> > polymorphism, I have been obliged to introduce access types.
> 
> Yes, this is generally the case in Ada 95.

Right.  Access types do seem necessary for inheritance-style inclusion 
polymorphism required for dynamic binding in Ada.  However, access types 
are not necessary to achieve generics-based parametric polymorhphism.  While
generics without exposed use of access types only provide static binding
in Ada, this approach can have a number of advantages (assuming a good
generics-savy compiler).  

> > And now by bringing in access types I have imported the problems
> > that go with them.
> 
> You are using an access type in a very restricted and disciplined way.
> I'm not sure what you mean by "the problems that go with them", but the
> fact that undisciplined use of access types may cause problems is
> irrelevant.  Your solution does not involve any such problems.

Well, even if you have a strong discipline that avoids aliasing-related
reasoning problems which access types can introduce, I personally think
that the code is just more difficult to read and understand when access
types are used as much as dynamic dispatching in Ada seems to require.

> > 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.
> 
> I don't understand the problem.  The need for the deallocation operation
> is encapsulated in the package.  The writer of the package ensures that
> there is never more than one object allocated.  The user of the type
> never has to know about it.  Indeed, the declaration of your access type
> ought to be moved out of the package declaration and into the package
> body.

Yep.  That's an important part of a strong discipline for software
component engineering.

Dave
--
dgibson@cis.ohio-state.edu




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

* Re: Cumbersome Polymorphism
  1997-01-27  0:00 ` Norman H. Cohen
  1997-01-28  0:00   ` Jon S Anthony
  1997-01-28  0:00   ` Dave Gibson
@ 1997-01-28  0:00   ` Richard Irvine
  2 siblings, 0 replies; 12+ messages in thread
From: Richard Irvine @ 1997-01-28  0:00 UTC (permalink / raw)



As usual, many thanks for your reply.

My basic point is that in order to make  simple use of polymorphism
the user of tagged types has to allocate and deallocate storage.
If, due to programming errors, deallocation is not done or not done
in the right place, the consequence can be memory leaks or dangling
references. (One of Ada's fortes is its static type checking, which
commends it for use in systems which must be highly reliable. The static
type checking is an important contribution to reliability, but I can
undo all the benefit to be gained from this, if I forget to deallocate
something or deallocate it in the wrong place). Also, by requiring the
routine use of access types, one might end up going down the slippery
road of passing access types around a program without being sure 
where or when they should be deallocated.
 
It would be good if the memory management associated with a tagged type
could be taken care of in the (single) package in which the type is 
defined, not in the (multiple) places where it is used, if not, every
polymorphic use of the type is an invitation to make deallocation
errors.
If I have a variable which holds a shape of some kind, I want to be able
to assign a triangle or a square to it without having to think about
allocation or deallocation, and without having the opportunity to get it
wrong.
   
In his posting, Robert Dewar gives the example of Ada.Unbounded.Strings.
The user of Unbounded_String does not have to allocate or deallocate the
String which Unbounded_String actually points to. I can assign one
Unbounded_String to another, oblivious to the fact that storage is
allocated and deallocated in the process. 
This certainly shows how to hide allocation and deallocation which gives
me an idea: why not put the allocation and deallocation in a generic 
controlled wrapper, e.g.


with Ada.Finalization;
use  Ada.Finalization; 

generic

   type User_Type (<>) is private;

package Controlled_Wrapper_P is

   type Controlled_Wrapper is private;

   function  Wrap
           ( The_User_Type          : User_Type          )
   return                             Controlled_Wrapper;

   function  Unwrap
           ( The_Controlled_Wrapper : Controlled_Wrapper )
   return                             User_Type;
 
private

   type Access_User_Type is access User_Type;

   type Controlled_Wrapper
      is new Controlled
      with record
         Reference : Access_User_Type;
      end record;

   procedure Initialize ( Object : in out Controlled_Wrapper );
   procedure Adjust     ( Object : in out Controlled_Wrapper );
   procedure Finalize   ( Object : in out Controlled_Wrapper );

end;


Renaming the types given before to something more homely:


package Shapes is

  type Shape is abstract tagged null record;

  procedure Do_Something_To( The_Shape    : Shape    )
  is abstract;

  type Triangle is new Shape with null record;
  procedure Do_Something_To( The_Triangle : Triangle );

  type Square   is new Shape with null record;
  procedure Do_Something_To( The_Square   : Square   );

end;


and instantiating the generic for Shape'Class


with Shapes;
use  Shapes; 
with Controlled_Wrapper_P;

package Shape_Wrapper_P
   is new Controlled_Wrapper_P(User_Type => Shape'Class);


Now I can make use of polymorphism with the allocation and deallocation
done by the controlled wrapper, i.e., 


with Shapes;
use  Shapes;
with Shape_Wrapper_P;
use  Shape_Wrapper_P;

procedure Main_Procedure

is

   The_Triangle : Triangle;
   The_Square   : Square;

   type Shape_Wrapper is new Shape_Wrapper_P.Controlled_Wrapper;

   The_Wrapped_Triangle : Shape_Wrapper := Wrap(The_Triangle);
   The_Wrapped_Square   : Shape_Wrapper := Wrap(The_Square  );

   The_Wrapped_Shape    : Shape_Wrapper;

begin

   The_Wrapped_Shape := The_Wrapped_Triangle;
   Do_Something_To(The_Shape => Unwrap(The_Wrapped_Shape));
   -- calls Do_Something_To for Triangle

   The_Wrapped_Shape := The_Wrapped_Square;
   Do_Something_To(The_Shape => Unwrap(The_Wrapped_Shape));
   -- calls Do_Something_To for Square

end;




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

* Re: Cumbersome Polymorphism
  1997-01-27  0:00 ` Norman H. Cohen
@ 1997-01-28  0:00   ` Jon S Anthony
  1997-01-28  0:00   ` Dave Gibson
  1997-01-28  0:00   ` Richard Irvine
  2 siblings, 0 replies; 12+ messages in thread
From: Jon S Anthony @ 1997-01-28  0:00 UTC (permalink / raw)



In article <32ECF2A3.6371@watson.ibm.com> "Norman H. Cohen" <ncohen@watson.ibm.com> writes:

> Richard Irvine wrote:
> > 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.
> 
> That is precisely the right approach:
>
>[...example...] 

This, of course, only works if the object is not referenced by other
such instances of the access type.  That issue is probably at the root
of Richard's concern.


> > Of course, Ada provides a mechanism for automating deallocation,
> > using controlled types, so a better solution would make use of this:
> 
> The use of controlled types is not warranted in this situation.

The problem is, it is not of any particularly good use in the more
general case either (the one where there are multiple refs).  It's the
same old story.  You need GC.  Or better yet, pool specific (type
specific) GC variants.

/Jon
-- 
Jon Anthony
Organon Motives, Inc.
Belmont, MA 02178
617.484.3383
jsa@organon.com





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

* Re: Cumbersome Polymorphism
  1997-01-27  0:00   ` Jon S Anthony
@ 1997-01-29  0:00     ` Robert Dewar
  0 siblings, 0 replies; 12+ messages in thread
From: Robert Dewar @ 1997-01-29  0:00 UTC (permalink / raw)



"> It is perfectly possible to hide allocation and deallocatoin from an
> Ada programmer -- just look at Ada.Unbounded.Strings as an example.
> Note that this also shows that variable length items can be handled
> in a completely transparent manner."


It is not at all clear that garbage collection would increase the efficiency
of an implementatoin of unbounded strings in the general case, what other
data do you have besides intuition for such a claim (and remember from my
SPITBOL background that I am a little familiar with GC, also see the 1977
paper in SP&E :-)





^ 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   ` Robert A Duff
1997-01-25  0:00     ` James O'Connor
1997-01-26  0:00       ` Brian Rogoff
1997-01-26  0:00       ` Robert Dewar
1997-01-25  0:00   ` James O'Connor
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   ` Jon S Anthony
1997-01-28  0:00   ` Dave Gibson
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