comp.lang.ada
 help / color / mirror / Atom feed
* Help - Constructors - ASAP.
@ 1998-07-15  0:00 Maxim Senin
  1998-07-16  0:00 ` Stephen Leake
  0 siblings, 1 reply; 26+ messages in thread
From: Maxim Senin @ 1998-07-15  0:00 UTC (permalink / raw)


How to write constructor?

I tried to define constraints in type def, but if  have derived type, this will
not work:

type    A (param1, param2 : TypeOfParam) is new BaseType with record
        fld1 : TypeOfParam := param1;
        fld2 : TypeOfParam := param2;
    end record;

type    B (param : AnotherType) is new A with record    -- how can I set fld1
and fld2 using constraints?
        fld3 : AnotherType := param;
    end record;

Furthermore, if A and B are private types, then following code will also not
compile:

private
    type A.......;
    type B... is new A.....;

instanceOfA := new B' (fld1 => ..., fld2 => ..., fld3 => ...);
-- error: expected private type "B"
-- found a composite type

So, I think, constraints will not work. I see the following way to handle this:
types can defined public or setters must be provided. 1st approach is not good -
for example, if I want the object to be constucted once, and then be read-only.
For example, an event object is constucted once, and then clients can read data
from it:

    event := new Event (eventSource, eventType, eventData);
    ...
    if ((getSource (event) = this'Access) and (getType (event) = KEY_PRESSED))
then
        ...

Will anybody tell me the way to provide constuctors for objects in this manner?

I thought about defining functions:

function newEvent (eventSource : Object, eventType : EventTypes, eventData :
EventDataType) return Event is
ret    : Event;
begin
    ret := new Event;
    ret.source := eventSource;
    return  (eventSource, eventType, eventData);
end;

Hope to hear from you...

Maxim




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

* Re: Help - Constructors - ASAP.
  1998-07-15  0:00 Maxim Senin
@ 1998-07-16  0:00 ` Stephen Leake
  0 siblings, 0 replies; 26+ messages in thread
From: Stephen Leake @ 1998-07-16  0:00 UTC (permalink / raw)


Maxim Senin <maks@tech.swh.lv> writes:

> How to write constructor?

> <snip> 

> I thought about defining functions:
> 
> function newEvent (eventSource : Object, eventType : EventTypes, eventData :
> EventDataType) return Event is
> ret    : Event;
> begin
>     ret := new Event;
>     ret.source := eventSource;
>     return  (eventSource, eventType, eventData);
> end;
> 
> Hope to hear from you...

Better is:

type Event is record ... end record;
type Event_Access is access Event;

function newEvent (Param1 : Param1_type) return Event_Access is
begin
    return new Event (Param1);
end newEvent;

or, if Event is limited, or you have some Init procedure:

function newEvent (Param1 : Param1_type) return Event_Access is
    temp : event_Access := new Event;
begin
    Init (temp.all, Param1);
    return temp;
end newEvent;

> 
> Maxim




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

* Help - Constructors - ASAP.
@ 1998-07-23  0:00 Maxim Senin
  1998-07-26  0:00 ` Matthew Heaney
  0 siblings, 1 reply; 26+ messages in thread
From: Maxim Senin @ 1998-07-23  0:00 UTC (permalink / raw)


How to write constructor?

I tried to define constraints in type def, but if  have derived type, this
will not work:

type    A (param1, param2 : TypeOfParam) is new BaseType with record
        fld1 : TypeOfParam := param1;
        fld2 : TypeOfParam := param2;
    end record;

type    B (param : AnotherType) is new A with record    -- how can I set
fld1 and fld2 using constraints?
        fld3 : AnotherType := param;
    end record;

Furthermore, if A and B are private types, then following code will also not
compile:

private
    type A.......;
    type B... is new A.....;

instanceOfA := new B' (fld1 => ..., fld2 => ..., fld3 => ...);
-- error: expected private type "B"
-- found a composite type

So, I think, constraints will not work. I see the following way to handle
this: types can defined public or setters
must be provided. 1st approach is not good - for example, if I want the
object to be constucted once, and then
be read-only. For example, an event object is constucted once, and then
clients can read data from it:

    event := new Event (eventSource, eventType, eventData);
    ...
    if ((getSource (event) = this'Access) and (getType (event) =
KEY_PRESSED)) then
        ...

Will anybody tell me the way to provide constuctors for objects in this
manner?

I thought about defining functions:

function newEvent (eventSource : Object, eventType : EventTypes, eventData :
EventDataType) return Event is
ret    : Event;
begin
    ret := new Event;
    ret.source := eventSource;
    return  (eventSource, eventType, eventData);
end;

Hope to hear from you...

Maxim









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

* Re: Help - Constructors - ASAP.
  1998-07-23  0:00 Help - Constructors - ASAP Maxim Senin
@ 1998-07-26  0:00 ` Matthew Heaney
  1998-07-27  0:00   ` dennison
  0 siblings, 1 reply; 26+ messages in thread
From: Matthew Heaney @ 1998-07-26  0:00 UTC (permalink / raw)


"Maxim Senin" <maks.tech.swh.lv> writes:

> I thought about defining functions:
> 
> function newEvent (eventSource : Object, eventType : EventTypes, eventData :
> EventDataType) return Event is
> ret    : Event;
> begin
>     ret := new Event;
>     ret.source := eventSource;
>     return  (eventSource, eventType, eventData);
> end;


In general, functions in Ada serve the role that constructors play in
other languages.  Your New_Event function is one example of a
constructor.

If the return type is tagged, then do NOT return the specific type as
the return type of the function.  Either return the classwide type, or
declare the constructors in a nested package called Constructors.  This
way, the functions won't be primitive operations for the type, and
therefore won't be inherited during derivation.









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

* Re: Help - Constructors - ASAP.
  1998-07-26  0:00 ` Matthew Heaney
@ 1998-07-27  0:00   ` dennison
  1998-07-30  0:00     ` Robert I. Eachus
  0 siblings, 1 reply; 26+ messages in thread
From: dennison @ 1998-07-27  0:00 UTC (permalink / raw)


In article <m3lnpg7j5s.fsf@mheaney.ni.net>,
  Matthew Heaney <matthew_heaney@acm.org> wrote:
> If the return type is tagged, then do NOT return the specific type as
> the return type of the function.  Either return the classwide type, or
> declare the constructors in a nested package called Constructors.  This
> way, the functions won't be primitive operations for the type, and
> therefore won't be inherited during derivation.

Hmm. You have probably been through this more than I, but it would think it
would be useful in some circumstances to have the function be inheritable. If
you need a new version of the "constructor" for a new derived type, you can
override the old one and the client won't be able to call the old one anymore.
If you make the original "constructor" class-wide instead, clients will always
be able to call it with any derived type.

T.E.D.

-----== Posted via Deja News, The Leader in Internet Discussion ==-----
http://www.dejanews.com/rg_mkgrp.xp   Create Your Own Free Member Forum




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

* Re: Help - Constructors - ASAP.
  1998-07-27  0:00   ` dennison
@ 1998-07-30  0:00     ` Robert I. Eachus
  1998-07-30  0:00       ` tedennison
  1998-08-01  0:00       ` Robert Dewar
  0 siblings, 2 replies; 26+ messages in thread
From: Robert I. Eachus @ 1998-07-30  0:00 UTC (permalink / raw)


In article <6pi4jq$j73$1@nnrp1.dejanews.com> dennison@telepath.com writes:

  > Hmm. You have probably been through this more than I, but it would
  > think it would be useful in some circumstances to have the
  > function be inheritable. If you need a new version of the
  > "constructor" for a new derived type, you can override the old one
  > and the client won't be able to call the old one anymore.  If you
  > make the original "constructor" class-wide instead, clients will
  > always be able to call it with any derived type.

    There are two reasons for following the advice.  First is that Ada
has a rule that inherited functions with a controlling result are
abstract, so they must be overridden.  Yes, there are cases where
constructor functions can be safely inherited from the parent, but
this rule makes you think it through. Second is that when you do want
a constructor which can return any type in the class, the right thing
to do usually is to make it a classwide type.

     So you normally finding yourself either defining a classwide
constructor or making the constructors non-primitive so they are not
derived.  For example:

    package Mixed_Lists is

      type Element_Kind is (Integer_Item, Float_Item, String_Item);
      type List is private;
      type List_Element is tagged private;
      type Integer_Element is new List_Element with private;
      type Float_Element is new List_Element with private;
      type String_Element is new List_Element with private;

      function New_Element(Kind: in Element_Kind) 
                                     return List_Element'Class;
      ...
    private
      ...
    end Mixed_Lists;

    package Mixed_List.Constructors is

      function New_Element(Value: Integer := 0) return Integer_Element;
      function New_Element(Value: Float := 0.0) return Float_Element;
      function New_Element(Value: String := "") return String_Element;

    end Mixed_List.Constructors;

    This example is contrived, becuase it uses both models and you
usually want one or the other.  (And for a type like this, you would
probably want direct append operations rather than explicit
constructors for the list elements.)



--

					Robert I. Eachus

with Standard_Disclaimer;
use  Standard_Disclaimer;
function Message (Text: in Clever_Ideas) return Better_Ideas is...




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

* Re: Help - Constructors - ASAP.
  1998-07-30  0:00     ` Robert I. Eachus
@ 1998-07-30  0:00       ` tedennison
  1998-07-31  0:00         ` Mats Weber
  1998-07-31  0:00         ` Matthew Heaney
  1998-08-01  0:00       ` Robert Dewar
  1 sibling, 2 replies; 26+ messages in thread
From: tedennison @ 1998-07-30  0:00 UTC (permalink / raw)


In article <EACHUS.98Jul30114523@spectre.mitre.org>,
  eachus@spectre.mitre.org (Robert I. Eachus) wrote:
> In article <6pi4jq$j73$1@nnrp1.dejanews.com> dennison@telepath.com writes:
>
>     There are two reasons for following the advice.  First is that Ada
> has a rule that inherited functions with a controlling result are
> abstract, so they must be overridden.  Yes, there are cases where
> constructor functions can be safely inherited from the parent, but
> this rule makes you think it through. Second is that when you do want
> a constructor which can return any type in the class, the right thing
> to do usually is to make it a classwide type.

But what happens in the situation where the client (perhaps a different
developer) wants to make a child class with fields that *must* be
initialized? That seems a quite reasonable thing to do. But now the
class-wide "constructor" isn't valid anymore. It can still be called for this
new child object, but will return a garbage value.

The only way I can see around this problem is to *also* declare a primitive
constructor, and have the body of the class-wide consructor redispatch to
that.

T.E.D.

-----== Posted via Deja News, The Leader in Internet Discussion ==-----
http://www.dejanews.com/rg_mkgrp.xp   Create Your Own Free Member Forum




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

* Re: Help - Constructors - ASAP.
  1998-07-30  0:00       ` tedennison
@ 1998-07-31  0:00         ` Mats Weber
  1998-07-31  0:00           ` dennison
  1998-07-31  0:00           ` Robert I. Eachus
  1998-07-31  0:00         ` Matthew Heaney
  1 sibling, 2 replies; 26+ messages in thread
From: Mats Weber @ 1998-07-31  0:00 UTC (permalink / raw)


tedennison@my-dejanews.com wrote:

> But what happens in the situation where the client (perhaps a different
> developer) wants to make a child class with fields that *must* be
> initialized? That seems a quite reasonable thing to do. But now the
> class-wide "constructor" isn't valid anymore. It can still be called for this
> new child object, but will return a garbage value.

That garbage value will be a value of the parent type, and assigning it to an
object of the Child type, or to an object of type Child'Class, will raise
Constraint_Error, so the problem will not go undetected. If your contructors
are not class-wide and not primitive, then you detect the problem at compile time.

> The only way I can see around this problem is to *also* declare a primitive
> constructor, and have the body of the class-wide consructor redispatch to
> that.

If you do that, you will run into all sorts of ambiguous call situations where
you will have to qualify your expressions.






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

* Re: Help - Constructors - ASAP.
  1998-07-31  0:00         ` Mats Weber
@ 1998-07-31  0:00           ` dennison
  1998-08-01  0:00             ` Robert Dewar
                               ` (2 more replies)
  1998-07-31  0:00           ` Robert I. Eachus
  1 sibling, 3 replies; 26+ messages in thread
From: dennison @ 1998-07-31  0:00 UTC (permalink / raw)


In article <35C1043E.9FFB23D0@elca-matrix.ch>,
  Mats.Weber@elca-matrix.ch wrote:
> tedennison@my-dejanews.com wrote:
>
> > But what happens in the situation where the client (perhaps a different
> > developer) wants to make a child class with fields that *must* be
> > initialized? That seems a quite reasonable thing to do. But now the
> > class-wide "constructor" isn't valid anymore. It can still be called for
this
> > new child object, but will return a garbage value.
>
> That garbage value will be a value of the parent type, and assigning it to an
> object of the Child type, or to an object of type Child'Class, will raise
> Constraint_Error, so the problem will not go undetected. If your contructors
> are not class-wide and not primitive, then you detect the problem at compile
time.
>

I see. I thought you were somehow passing back an object of the right class
for the context.

But, a naieve reader would look at that and think that they could get back an
object of *any* type in the class (as I have just proven :-) ), when in
actuality they always get back the same class of object.

If I read you correctly, what you were worried about was developers screwing
up and forgetting to override the default constructors. Nothing about this
solution prevents that. Now instead of getting the default constructor, our
hapless client will compile with the class-wide constructor. They get an
exception at runtime either way. But now the client, who is just as capable
of screwing up as the subclass author, could goof and use the class-wide
constructor on his own.

If your worry was inheriting constructors when their parameter profile in no
longer viable for constructing a subclass, I can see part of your point. But I
think I'd rather just have said subclasses override the constructor with a
routine that applies sensible defaults (or raises Constraint_Error).

Hmm. I believe the language rules state that no more primitive operations may
be declared after the type is "finalized". It may be possible to force this by
doing something like deriving a dummy subclass before the constructor is
declared.

T.E.D.

-----== Posted via Deja News, The Leader in Internet Discussion ==-----
http://www.dejanews.com/rg_mkgrp.xp   Create Your Own Free Member Forum




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

* Re: Help - Constructors - ASAP.
  1998-07-31  0:00         ` Mats Weber
  1998-07-31  0:00           ` dennison
@ 1998-07-31  0:00           ` Robert I. Eachus
  1998-08-01  0:00             ` Matthew Heaney
  1 sibling, 1 reply; 26+ messages in thread
From: Robert I. Eachus @ 1998-07-31  0:00 UTC (permalink / raw)


   tedennison@my-dejanews.com wrote:

   > But what happens in the situation where the client (perhaps a
   > different developer) wants to make a child class with fields that
   > *must* be initialized? That seems a quite reasonable thing to
   > do. But now the class-wide "constructor" isn't valid anymore. It
   > can still be called for this new child object, but will return a
   > garbage value.

   Note that there ARE cases where you do want to have a dispatching
constructor.   In those cases you really do want to have a constructor
that is derived, and you (or someone else) will have to do the
explicit overriding.  Using records with default initial values can
make this fairly painless, but you want to have derivation trees that
are wide instead of deep.  (You can even use generics to create the
instances.)
--

					Robert I. Eachus

with Standard_Disclaimer;
use  Standard_Disclaimer;
function Message (Text: in Clever_Ideas) return Better_Ideas is...




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

* Re: Help - Constructors - ASAP.
  1998-07-30  0:00       ` tedennison
  1998-07-31  0:00         ` Mats Weber
@ 1998-07-31  0:00         ` Matthew Heaney
  1 sibling, 0 replies; 26+ messages in thread
From: Matthew Heaney @ 1998-07-31  0:00 UTC (permalink / raw)


tedennison@my-dejanews.com writes:

> But what happens in the situation where the client (perhaps a different
> developer) wants to make a child class with fields that *must* be
> initialized? That seems a quite reasonable thing to do. But now the
> class-wide "constructor" isn't valid anymore. It can still be called for this
> new child object, but will return a garbage value.

No.  It will bomb (by raising CE) at the point of assignment, because of
a tag mismatch.  You'd be trying to assign an value of the parent type
to an object of the child type, which is illegal, and you'll therefore
get a tag mismatch at run-time.

> The only way I can see around this problem is to *also* declare a primitive
> constructor, and have the body of the class-wide consructor redispatch to
> that.

Never declare a constructor that is a primitive operation of the type.




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

* Re: Help - Constructors - ASAP.
  1998-07-31  0:00           ` dennison
  1998-08-01  0:00             ` Robert Dewar
@ 1998-08-01  0:00             ` Matthew Heaney
  1998-08-03  0:00             ` Mats Weber
  2 siblings, 0 replies; 26+ messages in thread
From: Matthew Heaney @ 1998-08-01  0:00 UTC (permalink / raw)


dennison@telepath.com writes:

> But, a naieve reader would look at that and think that they could get
> back an object of *any* type in the class (as I have just proven :-)
> ), when in actuality they always get back the same class of object.

If you don't like returning the class-wide type, then declare a
constructor that returns the specific type, but is not primitive.  The
simplest way to do this is to declare the constructors in a nested
package:

package P is

   type T is tagged private;

   <primitive ops>

   package Constructors is

      function New_T (I : Integer) return T;

   end Constructors;

private
...
end P;


> If I read you correctly, what you were worried about was developers
> screwing up and forgetting to override the default
> constructors. Nothing about this solution prevents that. Now instead
> of getting the default constructor, our hapless client will compile
> with the class-wide constructor. They get an exception at runtime
> either way. But now the client, who is just as capable of screwing up
> as the subclass author, could goof and use the class-wide constructor
> on his own.

You can detect type mismatches at compile-time by using a constructor
that returns the specific type (not the class-wide type), and is
non-primitive.  See the example above.

> Hmm. I believe the language rules state that no more primitive
> operations may be declared after the type is "finalized". It may be
> possible to force this by doing something like deriving a dummy
> subclass before the constructor is declared.

I'm not sure what you mean.  If you want non-primitive operations, then
just declare them in a nested package.  Don't use the freezing rules
(see RM95 13.14) to try to do this; that would be confusing.





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

* Re: Help - Constructors - ASAP.
  1998-07-30  0:00     ` Robert I. Eachus
  1998-07-30  0:00       ` tedennison
@ 1998-08-01  0:00       ` Robert Dewar
  1998-08-01  0:00         ` Matthew Heaney
  1998-08-03  0:00         ` tedennison
  1 sibling, 2 replies; 26+ messages in thread
From: Robert Dewar @ 1998-08-01  0:00 UTC (permalink / raw)


Robert Eachus says

<<    There are two reasons for following the advice.  First is that Ada
has a rule that inherited functions with a controlling result are
abstract, so they must be overridden.  Yes, there are cases where
constructor functions can be safely inherited from the parent, but
this rule makes you think it through. Second is that when you do want
a constructor which can return any type in the class, the right thing
to do usually is to make it a classwide type.
>>


Well, the cases where safe inheritance would be feasible are limited to
extensions of the type with no additional fields, not very useful!





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

* Re: Help - Constructors - ASAP.
  1998-07-31  0:00           ` dennison
@ 1998-08-01  0:00             ` Robert Dewar
  1998-08-01  0:00               ` Matthew Heaney
  1998-08-01  0:00             ` Matthew Heaney
  1998-08-03  0:00             ` Mats Weber
  2 siblings, 1 reply; 26+ messages in thread
From: Robert Dewar @ 1998-08-01  0:00 UTC (permalink / raw)


<<But, a naieve reader would look at that and think that they could get back an
object of *any* type in the class (as I have just proven :-) ), when in
actuality they always get back the same class of object.
>>

That's a pretty fundamental misunderstanding of what classwide programming
is all about. Yes, of course people can have all sorts of misconceptions,
but this one is pretty basic, and not a misconception that could stay
arround very long!





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

* Re: Help - Constructors - ASAP.
  1998-07-31  0:00           ` Robert I. Eachus
@ 1998-08-01  0:00             ` Matthew Heaney
  0 siblings, 0 replies; 26+ messages in thread
From: Matthew Heaney @ 1998-08-01  0:00 UTC (permalink / raw)


eachus@spectre.mitre.org (Robert I. Eachus) writes:

>    Note that there ARE cases where you do want to have a dispatching
> constructor.   In those cases you really do want to have a constructor
> that is derived, and you (or someone else) will have to do the
> explicit overriding.

Note that there are two kinds of dispatching: dispatch on the return
type, and dispatch on a parameter.

Bob is talking about the first case, dispatching on the return type.  A
common example of this sort of thing is a set type with a constructor
that returns the empty set:

generic
   type Set_Item is private;
   with function "=" (L, R : Set_Item) return Boolean is <>;
package Sets is

   type Root_Set is abstract tagged null record;

   function Empty_Set return Root_Set'Class;

   <other primitive ops>
...
end Sets;


I might use the constructor like this:

procedure Op (Set : in Root_Set'Class) is

   Local : Set'Class := Set;

begin

   <do some stuff>

   Local := Empty_Set;

   <do some more stuff>

end Op;


The function Empty_Set is a constructor that dispatches on the tag of
object Local.  Here is a case where you really want to have a
constructor that returns the specific type.  Each non-abstract type that
derives from Root_Set will have to provide an Empty_Set constructor.

That's an example of the first kind of constructor.  The other kind of
constructor returns a class-wide object, and dispatches on a parameter.
An example of this kind of constructor is a "factory method."

Consider our set type again.  Suppose we have an operation that takes a
class-wide parameter, and we want to iterate over that set.  What we can
do is have the set provide a factory method to construct an iterator
that can be used to iterate over that that kind of set.

Let's add an iterator type to our example:

generic
...
package Sets is

   type Root_Set is abstract tagged null record;

   type Set_Iterator is abstract tagged null record;

   function New_Iterator 
     (Set : access Root_Set) return Set_Iterator'Class;


   function Is_Done
     (Iterator : Set_Iterator) return Boolean is abstract;

   function Get_Item 
     (Iterator : Set_Iterator) return Set_Item is abstract;

   procedure Advance 
     (Iterator : in out Set_Iterator) is abstract;
...
end Sets;


Note that the constructor, New_Iterator, returns a class-wide type.

Let's use our constructor to implement a passive iterator that works for
any type in the set class.  Here's the spec:

generic
   with procedure Process
     (Item : in     Set_Item;
      Done : in out Boolean);
procedure For_Every_Item (Set : access Root_Set'Class);


Note how the Set parameter is class-wide.  Here's the body:

procedure For_Every_Item (Set : access Root_Set'Class) is

   Iterator : Set_Iterator'Class := New_Iterator (Set);

   Done : Boolean := False;

begin

   while not Is_Done (Iterator) loop

      Process (Get_Item (Iterator), Done);

      exit when Done;

      Advance (Iterator);

   end loop;

end For_Every_Item;


The New_Iterator constructor dispatches on the Set parameter.  Each of
the iterator operations (like Get_Item) then dispatches on the tag of
the Iterator object.

There you have it.  Two examples of constructors, one returning a
specific type, the other returning a class-wide type.  Both techniques
are appropriate for different kinds of problems.

Happy constructing!

Matt





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

* Re: Help - Constructors - ASAP.
  1998-08-01  0:00       ` Robert Dewar
@ 1998-08-01  0:00         ` Matthew Heaney
  1998-08-03  0:00           ` Mats Weber
  1998-08-10  0:00           ` Robert I. Eachus
  1998-08-03  0:00         ` tedennison
  1 sibling, 2 replies; 26+ messages in thread
From: Matthew Heaney @ 1998-08-01  0:00 UTC (permalink / raw)


dewar@merv.cs.nyu.edu (Robert Dewar) writes:

> Well, the cases where safe inheritance would be feasible are limited to
> extensions of the type with no additional fields, not very useful!

This is the Ada 95 counterpart to the "Transitivity of Visibility"
technique described in the Ada 83 Rationale.

This is helpful when you have a type that comes into existence because
of a nested instantation.  For example:

generic
...
package GQ is 

   type T is tagged ...;
...
end GQ;

with GQ;
pragma Elaborate_All (GQ);

package P is
...
   package Q is new GQ (...);

   type T is new Q.T with null record;
...
end P;

What you do NOT want to happen is to for a client to have to refer to
P.Q.T.  The client shouldn't have to care that T was brought into
existence because of an instantiation; it's really an implementation
detail.  By using transitivity of visibility, the client can just refer
to P.T, in effect "hiding" the fact that T comes from Q.

You can do this with non-tagged types too.  One common mistake I see
programmers make is to do this:

package P is

   package Bounded_Strings_20 is
     new Ada.Strings.Bounded.Generic_Bounded_Length (20);
...
end P;

Now every client in the universe has to refer to the string type as
P.Bounded_Strings_20.Bounded_String.  Why should a client care?  

Use the transitivity of visibility technique to bring the string type
directly in the scope of P:

package P is

   package Bounded_Strings_20 is
     new Ada.Strings.Bounded.Generic_Bounded_Length (20);

   type Bounded_String is
     new Bounded_Strings_20.Bounded_String;
...
end P;

Now a client can just refer to P.Bounded_String.

Ah, simple.  Life is good.

Matt





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

* Re: Help - Constructors - ASAP.
  1998-08-01  0:00             ` Robert Dewar
@ 1998-08-01  0:00               ` Matthew Heaney
  0 siblings, 0 replies; 26+ messages in thread
From: Matthew Heaney @ 1998-08-01  0:00 UTC (permalink / raw)


dewar@merv.cs.nyu.edu (Robert Dewar) writes:

> <<But, a naieve reader would look at that and think that they could
> get back an object of *any* type in the class (as I have just proven
> :-) ), when in actuality they always get back the same class of
> object.
> >>
> 
> That's a pretty fundamental misunderstanding of what classwide
> programming is all about. Yes, of course people can have all sorts of
> misconceptions, but this one is pretty basic, and not a misconception
> that could stay arround very long!

I'm with Robert on this one.  You have to recognize something as an
idiom.  When you see a function

   function New_T return T'Class;

you just have to "know" that this is a constructor for T, and for T
only.  Once you recognize the idiom, you know instantly that

   type NT is new T with private;
...
   O : NT := New_T;

is wrong, even though the compiler won't tell you it's wrong.




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

* Re: Help - Constructors - ASAP.
  1998-08-01  0:00       ` Robert Dewar
  1998-08-01  0:00         ` Matthew Heaney
@ 1998-08-03  0:00         ` tedennison
  1 sibling, 0 replies; 26+ messages in thread
From: tedennison @ 1998-08-03  0:00 UTC (permalink / raw)


In article <dewar.901945397@merv>,
  dewar@merv.cs.nyu.edu (Robert Dewar) wrote:
> Robert Eachus says
>
> <<    There are two reasons for following the advice.  First is that Ada
> has a rule that inherited functions with a controlling result are
> abstract, so they must be overridden.  Yes, there are cases where
> constructor functions can be safely inherited from the parent, but
> this rule makes you think it through. Second is that when you do want
> a constructor which can return any type in the class, the right thing
> to do usually is to make it a classwide type.
> >>
>
> Well, the cases where safe inheritance would be feasible are limited to
> extensions of the type with no additional fields, not very useful!

..or with only defaulted fields, or fields where sensible defaults exist. But
yes, I'm beginning to see the light here.

T.E.D.

-----== Posted via Deja News, The Leader in Internet Discussion ==-----
http://www.dejanews.com/rg_mkgrp.xp   Create Your Own Free Member Forum




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

* Re: Help - Constructors - ASAP.
  1998-08-01  0:00         ` Matthew Heaney
@ 1998-08-03  0:00           ` Mats Weber
  1998-08-03  0:00             ` Matthew Heaney
  1998-08-10  0:00           ` Robert I. Eachus
  1 sibling, 1 reply; 26+ messages in thread
From: Mats Weber @ 1998-08-03  0:00 UTC (permalink / raw)


Matthew Heaney wrote:

> You can do this with non-tagged types too.  One common mistake I see
> programmers make is to do this:
> 
> package P is
> 
>    package Bounded_Strings_20 is
>      new Ada.Strings.Bounded.Generic_Bounded_Length (20);
> ...
> end P;
> 
> Now every client in the universe has to refer to the string type as
> P.Bounded_Strings_20.Bounded_String.  Why should a client care?
> 
> Use the transitivity of visibility technique to bring the string type
> directly in the scope of P:
> 
> package P is
> 
>    package Bounded_Strings_20 is
>      new Ada.Strings.Bounded.Generic_Bounded_Length (20);
> 
>    type Bounded_String is
>      new Bounded_Strings_20.Bounded_String;
> ...
> end P;
> 
> Now a client can just refer to P.Bounded_String.

This works as long as the package that implements the type exports only one
type and only primitive operations of that one type, but that is far from the
most common case, at least in my code. Take for instance Sequential_IO:

package P is

   package Implementation is new Sequential_IO(Whatever);

   type File_Type is new Implementation.File_Type;
   type File_Mode is ???; -- no clean way of doing this

end P;

Another problem, which I think is even worse, is when some operations of a
type are generics (e.g. iterators), then these will not be derived and you
will have to get them from the package that declares them.

Not to mention the fact that the rules on type derivation, primitive
operations, etc. are one of the obscure areas of the Ada language. Not to me,
I know how they work, but I have had to teach quite a few people to overtake
code that uses some (really not much) type derivation, and nobody seems to
find it natural. E.g. when you see

   X : P.T;

begin
   P.Op(X);

you expect to find a declaration for Op in P, right ?




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

* Re: Help - Constructors - ASAP.
  1998-07-31  0:00           ` dennison
  1998-08-01  0:00             ` Robert Dewar
  1998-08-01  0:00             ` Matthew Heaney
@ 1998-08-03  0:00             ` Mats Weber
  2 siblings, 0 replies; 26+ messages in thread
From: Mats Weber @ 1998-08-03  0:00 UTC (permalink / raw)


dennison@telepath.com wrote:

> I see. I thought you were somehow passing back an object of the right class
> for the context.

No, because you can't do this.

package Parent is

   type T is tagged private;

   function Construct return T'Class;
      -- Class-wide just to make it non-primitive.

end Parent;


with Parent;

package Child is

   type T is new Parent.T with private;

end Child;

As Parent has no knowledge of Child, its constructure cannot be modified to
return a Child.T. It will always return a Parent.T, which will raise
Constraint_Error in the following case:

declare
   X : Child.T := Parent.Construct;
begin
   ...

> But, a naieve reader would look at that and think that they could get back an
> object of *any* type in the class (as I have just proven :-) ), when in
> actuality they always get back the same class of object.

This means that you should not mess with tagged types as long as you are still
naive :-)

> If I read you correctly, what you were worried about was developers screwing
> up and forgetting to override the default constructors. Nothing about this
> solution prevents that. Now instead of getting the default constructor, our
> hapless client will compile with the class-wide constructor. They get an
> exception at runtime either way. But now the client, who is just as capable
> of screwing up as the subclass author, could goof and use the class-wide
> constructor on his own.

This seems to show that non-primitive _and_ non class-wide constructors are
preferable (e.g. using the nested package or child package approach).




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

* Re: Help - Constructors - ASAP.
  1998-08-03  0:00           ` Mats Weber
@ 1998-08-03  0:00             ` Matthew Heaney
  1998-08-03  0:00               ` Mats Weber
  0 siblings, 1 reply; 26+ messages in thread
From: Matthew Heaney @ 1998-08-03  0:00 UTC (permalink / raw)


Mats Weber <Mats.Weber@elca-matrix.ch> writes:

> > Now a client can just refer to P.Bounded_String.
> 
> This works as long as the package that implements the type exports only one
> type and only primitive operations of that one type, but that is far from the
> most common case, at least in my code. Take for instance Sequential_IO:
> 
> package P is
> 
>    package Implementation is new Sequential_IO(Whatever);
> 
>    type File_Type is new Implementation.File_Type;
>    type File_Mode is ???; -- no clean way of doing this
> 
> end P;

You bring up a very good point.  (This is same "feature" you highlighted
in your PhD.)  One rule of thumb is that, if possible, don't declare
anything in a generic except the "main" abstract data type.  Declare
supplementary types in another package - perhaps a non-generic root
package.  In your example, type File_Type (and Count) could have gone in
package Ada.  (Of course we can't really do that for Seq_IO, for obvious
reasons.  But consider doing this for packages you write.)
 
> Another problem, which I think is even worse, is when some operations of a
> type are generics (e.g. iterators), then these will not be derived and you
> will have to get them from the package that declares them.

You can rename generic operations.

> Not to mention the fact that the rules on type derivation, primitive
> operations, etc. are one of the obscure areas of the Ada language. Not to me,
> I know how they work, but I have had to teach quite a few people to overtake
> code that uses some (really not much) type derivation, and nobody seems to
> find it natural. E.g. when you see
> 
>    X : P.T;
> 
> begin
>    P.Op(X);
> 
> you expect to find a declaration for Op in P, right ?

Indeed, this is true.  Many Ada programmers don't have a clue about how
inheritence for non-tagged types works, and certainly don't take
advantage of it.  (And those who do use it, just confuse the others).

In another post a few weeks back, I observed that if people really
wanted to simplify Ada 83, then type derivation would have been a good
candidate for removal.  I think that this is one of those things that
only Jean wanted, and he used his veto power over the rest of the DRs to
keep it in the language.





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

* Re: Help - Constructors - ASAP.
  1998-08-03  0:00             ` Matthew Heaney
@ 1998-08-03  0:00               ` Mats Weber
  1998-08-04  0:00                 ` Matthew Heaney
  0 siblings, 1 reply; 26+ messages in thread
From: Mats Weber @ 1998-08-03  0:00 UTC (permalink / raw)


Matthew Heaney wrote:

> > Another problem, which I think is even worse, is when some operations of a
> > type are generics (e.g. iterators), then these will not be derived and you
> > will have to get them from the package that declares them.
> 
> You can rename generic operations.

But then, they keep the old parameter profile, the one with the type you are
deriving from, not the derived type. Bad.




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

* Re: Help - Constructors - ASAP.
  1998-08-04  0:00                 ` Matthew Heaney
@ 1998-08-04  0:00                   ` Mats Weber
  1998-08-05  0:00                     ` Matthew Heaney
  0 siblings, 1 reply; 26+ messages in thread
From: Mats Weber @ 1998-08-04  0:00 UTC (permalink / raw)


Matthew Heaney wrote:

> Ah, but then another rule of thumb applies.  Since the generic operation
> isn't primitive anyway, you just make the tagged parameters class-wide.
> Then it will work for your derived types too.

Weren't we talking about non-tagged types ?

Anyway, I don't think it's a good idea to make them class-wide just for that
reason. Most often, the generic operation needs to be adapted when inheriting,
so making the tagged type parameters class-wide is error-prone.




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

* Re: Help - Constructors - ASAP.
  1998-08-03  0:00               ` Mats Weber
@ 1998-08-04  0:00                 ` Matthew Heaney
  1998-08-04  0:00                   ` Mats Weber
  0 siblings, 1 reply; 26+ messages in thread
From: Matthew Heaney @ 1998-08-04  0:00 UTC (permalink / raw)


Mats Weber <Mats.Weber@elca-matrix.ch> writes:

>>> Another problem, which I think is even worse, is when some
>>> operations of a type are generics (e.g. iterators), then these will
>>> not be derived and you will have to get them from the package that
>>> declares them.
>> 
>> You can rename generic operations.
> 
> But then, they keep the old parameter profile, the one with the type you are
> deriving from, not the derived type. Bad.

Ah, but then another rule of thumb applies.  Since the generic operation
isn't primitive anyway, you just make the tagged parameters class-wide.
Then it will work for your derived types too.






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

* Re: Help - Constructors - ASAP.
  1998-08-04  0:00                   ` Mats Weber
@ 1998-08-05  0:00                     ` Matthew Heaney
  0 siblings, 0 replies; 26+ messages in thread
From: Matthew Heaney @ 1998-08-05  0:00 UTC (permalink / raw)


Mats Weber <Mats.Weber@elca-matrix.ch> writes:
> > Ah, but then another rule of thumb applies.  Since the generic operation
> > isn't primitive anyway, you just make the tagged parameters class-wide.
> > Then it will work for your derived types too.
> 
> Weren't we talking about non-tagged types ?

I thought tagged types.  At least some of the discussion on this thread
concerned whether have the constructor return the class-wide vs specific
type. 
 
> Anyway, I don't think it's a good idea to make them class-wide just
> for that reason. Most often, the generic operation needs to be adapted
> when inheriting, so making the tagged type parameters class-wide is
> error-prone.

Then we disagree.  If we're talking about a hierarchy of tagged types,
then there's not much difference between declaring a primitive operation
of the parent type that is automatically inherited by the derived type,
and manually renaming a generic operation that takes the parent
class-wide type.

Don't forget the example: we were deriving from the parent type by
adding a null extension, to get direct visibility of the type in a
different scope.  The types are otherwise identical.  So I don't accept
your argument that making the generic operation is error-prone, because
it's basically the same type.

Anyway, shouldn't an operation inherited from the parent always be
guaranteed to work in the derived type?  If there is an inherited
operation that wouldn't work in the derived type, then it's the
responsibility of the derived type to override the operation so that it
does work.

Ditto for parent generic operations that take the parent class-wide
type.  If the operation will work in the derived type, then the derived
type can safely rename the parent's generic operation.  If the operation
wouldn't work, then it is of course the responsibility of the derived
type to "override" the generic parent operation by providing another.

There is nothing special about the a parent operation being generic,
except that (I think) you should declare the operation to take
class-wide parameters, in order to simplify the life of programmer
writing the derived type.  (And to simplify the clients of the derived
type, who will then have visibility to all the operations, including
generic ones).

Issues about correctness or error-proneness apply equally, whether the
operation is inherited, or a renaming of a generic operation.






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

* Re: Help - Constructors - ASAP.
  1998-08-01  0:00         ` Matthew Heaney
  1998-08-03  0:00           ` Mats Weber
@ 1998-08-10  0:00           ` Robert I. Eachus
  1 sibling, 0 replies; 26+ messages in thread
From: Robert I. Eachus @ 1998-08-10  0:00 UTC (permalink / raw)


In article <m3vhodvevl.fsf@mheaney.ni.net> Matthew Heaney <matthew_heaney@acm.org> writes:

>   This is helpful when you have a type that comes into existence because
>   of a nested instantation.  For example...

>   with GQ;
>   pragma Elaborate_All (GQ);

>   package P is
>   ...
>      package Q is new GQ (...);

>      type T is new Q.T with null record;
       -- and this is the case where you want to inherit the construtor:
       function Create_T(...) return T;
       -- of course, if you forget to do this, the compiler will be
       -- sure to remind you.  ;-)
>  ...
>  end P;

   -- and also in the package body:
   function Create_T(...) return T is 
   begin return Q.T(Q.Create_T(...)); end Create_T;

   It is a bit of a pain when you need to do it right, but as you can
see, it amounts to a line in the spec and two in the body.

>  What you do NOT want to happen is to for a client to have to refer to
>  P.Q.T.  The client shouldn't have to care that T was brought into
>  existence because of an instantiation; it's really an implementation
>  detail.  By using transitivity of visibility, the client can just refer
>  to P.T, in effect "hiding" the fact that T comes from Q.

   Exactly, the user of the abstraction should never need to pay
attention to the man behind the curtin.
--

					Robert I. Eachus

with Standard_Disclaimer;
use  Standard_Disclaimer;
function Message (Text: in Clever_Ideas) return Better_Ideas is...




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

end of thread, other threads:[~1998-08-10  0:00 UTC | newest]

Thread overview: 26+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
1998-07-23  0:00 Help - Constructors - ASAP Maxim Senin
1998-07-26  0:00 ` Matthew Heaney
1998-07-27  0:00   ` dennison
1998-07-30  0:00     ` Robert I. Eachus
1998-07-30  0:00       ` tedennison
1998-07-31  0:00         ` Mats Weber
1998-07-31  0:00           ` dennison
1998-08-01  0:00             ` Robert Dewar
1998-08-01  0:00               ` Matthew Heaney
1998-08-01  0:00             ` Matthew Heaney
1998-08-03  0:00             ` Mats Weber
1998-07-31  0:00           ` Robert I. Eachus
1998-08-01  0:00             ` Matthew Heaney
1998-07-31  0:00         ` Matthew Heaney
1998-08-01  0:00       ` Robert Dewar
1998-08-01  0:00         ` Matthew Heaney
1998-08-03  0:00           ` Mats Weber
1998-08-03  0:00             ` Matthew Heaney
1998-08-03  0:00               ` Mats Weber
1998-08-04  0:00                 ` Matthew Heaney
1998-08-04  0:00                   ` Mats Weber
1998-08-05  0:00                     ` Matthew Heaney
1998-08-10  0:00           ` Robert I. Eachus
1998-08-03  0:00         ` tedennison
  -- strict thread matches above, loose matches on Subject: below --
1998-07-15  0:00 Maxim Senin
1998-07-16  0:00 ` Stephen Leake

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