comp.lang.ada
 help / color / mirror / Atom feed
* Constructing an object
@ 2005-09-21  8:46 Maciej Sobczak
  2005-09-21 10:16 ` Georg Bauhaus
                   ` (2 more replies)
  0 siblings, 3 replies; 34+ messages in thread
From: Maciej Sobczak @ 2005-09-21  8:46 UTC (permalink / raw)


Hi,

I'm learning Ada and I would like to better understand the ways used to 
create objects.

Let's say I have the following:

    type Color is (Black, Red, Green, Blue, White);

    type Shape is tagged record
       C : Color;
    end record;

    type Rectangle is new Shape with record
       A, B : Positive;
    end record;


 From what I've learnt ("Ada 95: The Craft of Object-Oriented 
Programming" by John English), there are two possible ways to implement 
"constructors" for these types:

1. By procedure, which is a primitive operation for each type in the 
hierarchy, for example:

    procedure New_Shape(C : in Color; S : out Shape) is
    begin
       S.C := C;
    end;

    procedure New_Rectangle(A, B : in Positive; C : in Color; R : out 
Rectangle) is
    begin
       New_Shape(C, Shape(R));
       R.A := A;
       R.B := B;
    end;

    -- another version (btw - which version do you prefer?)
    procedure New_Rectangle(A, B : in Positive; C : in Color; R : out 
Rectangle) is
       St : Shape;
    begin
       New_Shape(C, St);
       R := (St with A => A, B => B);
    end;


This does not please me much, because I can have uninitialized objects, 
which may not make sense in the program:

    R : Rectangle;

2. By function:

    function New_Shape(C : in Color) return Shape is
       S : Shape;
    begin
       S.C := C;
       return S;
    end;

    function New_Rectangle(A, B : in Positive; C : in Color) return 
Rectangle is
       R : Rectangle;
    begin
       Shape(R) := New_Shape(C);   -- is this OK?
       R.A := A;
       R.B := B;
       return R;
    end;

This does not please me for the same reason - no enforcement of proper 
initialization, if a given type makes no sense uninitialized.


What approach do you actually use in the real (non-tutorial) code?
Are there other techniques?
(I'm aware of the controlled types, but I need to provide parameters for 
construction, which the special Initialize procedure does not have.)


-- 
Maciej Sobczak : http://www.msobczak.com/
Programming    : http://www.msobczak.com/prog/



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

* Re: Constructing an object
  2005-09-21  8:46 Constructing an object Maciej Sobczak
@ 2005-09-21 10:16 ` Georg Bauhaus
  2005-09-22  7:21   ` Maciej Sobczak
  2005-09-21 11:55 ` Dmitry A. Kazakov
  2005-09-23  5:40 ` Matthew Heaney
  2 siblings, 1 reply; 34+ messages in thread
From: Georg Bauhaus @ 2005-09-21 10:16 UTC (permalink / raw)


Maciej Sobczak wrote:
> Hi,
> 
> I'm learning Ada and I would like to better understand the ways used to 
> create objects.

Very recently, there was a thread about this subject,
It mentions a way to force calling a construction
function. 
The subject was "tagged record child: override constructor?"



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

* Re: Constructing an object
  2005-09-21  8:46 Constructing an object Maciej Sobczak
  2005-09-21 10:16 ` Georg Bauhaus
@ 2005-09-21 11:55 ` Dmitry A. Kazakov
  2005-09-22  7:28   ` Maciej Sobczak
  2005-09-23  5:40 ` Matthew Heaney
  2 siblings, 1 reply; 34+ messages in thread
From: Dmitry A. Kazakov @ 2005-09-21 11:55 UTC (permalink / raw)


On Wed, 21 Sep 2005 10:46:35 +0200, Maciej Sobczak wrote:

> I'm learning Ada and I would like to better understand the ways used to 
> create objects.
> 
> Let's say I have the following:
> 
>     type Color is (Black, Red, Green, Blue, White);
> 
>     type Shape is tagged record
>        C : Color;
>     end record;
> 
>     type Rectangle is new Shape with record
>        A, B : Positive;
>     end record;
> 
>  From what I've learnt ("Ada 95: The Craft of Object-Oriented 
> Programming" by John English), there are two possible ways to implement 
> "constructors" for these types:
> 
> 1. By procedure, which is a primitive operation for each type in the 
> hierarchy, for example:
> 
>     procedure New_Shape(C : in Color; S : out Shape) is
>     begin
>        S.C := C;
>     end;
> 
>     procedure New_Rectangle(A, B : in Positive; C : in Color; R : out 
> Rectangle) is
>     begin
>        New_Shape(C, Shape(R));
>        R.A := A;
>        R.B := B;
>     end;
> 
>     -- another version (btw - which version do you prefer?)
>     procedure New_Rectangle(A, B : in Positive; C : in Color; R : out 
> Rectangle) is
>        St : Shape;
>     begin
>        New_Shape(C, St);
>        R := (St with A => A, B => B);
>     end;
> 
> 
> This does not please me much, because I can have uninitialized objects, 
> which may not make sense in the program:
> 
>     R : Rectangle;
> 
> 2. By function:
> 
>     function New_Shape(C : in Color) return Shape is
>        S : Shape;
>     begin
>        S.C := C;
>        return S;
>     end;
> 
>     function New_Rectangle(A, B : in Positive; C : in Color) return 
> Rectangle is
>        R : Rectangle;
>     begin
>        Shape(R) := New_Shape(C);   -- is this OK?
>        R.A := A;
>        R.B := B;
>        return R;
>     end;
> 
> This does not please me for the same reason - no enforcement of proper 
> initialization, if a given type makes no sense uninitialized.
> 
> What approach do you actually use in the real (non-tutorial) code?
> Are there other techniques?

3. The default values:

type Shape is tagged record
   C : Color := Black; -- This is initialized
end record;

4. Initialization enforcement via discriminants:

4.a. Discriminants as members:

type Shape (C : Color) is tagged null record;
   -- C has to be defined

4.b. Box-discriminants:

   type Shape (<>) is tagged private;
        -- Shape objects have to be initialized

private
   type Shape is tagged record
      C : Color;
   end record;

5. Abstract types with private descendants and a factory:

   type Abstract_Shape is abstract tagged null record;
   function New_Shape (...) return Abstract_Shape'Class;
      -- Factory is the only public way to create it
private
   type Shape is new Abstract_Shape with ...;

> (I'm aware of the controlled types, but I need to provide parameters for 
> construction, which the special Initialize procedure does not have.)

Alas, but Ada does not have user-defined constructors with parameters.
There are only constructing functions. You can use 2+4.b.

-- 
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de



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

* Re: Constructing an object
  2005-09-21 10:16 ` Georg Bauhaus
@ 2005-09-22  7:21   ` Maciej Sobczak
  0 siblings, 0 replies; 34+ messages in thread
From: Maciej Sobczak @ 2005-09-22  7:21 UTC (permalink / raw)


Georg Bauhaus wrote:

>> I'm learning Ada and I would like to better understand the ways used 
>> to create objects.
> 
> Very recently, there was a thread about this subject,
> It mentions a way to force calling a construction
> function. The subject was "tagged record child: override constructor?"

Indeed, thanks for reminding it. The interesting part of that thread 
just sunk in the noise of a flame war.


-- 
Maciej Sobczak : http://www.msobczak.com/
Programming    : http://www.msobczak.com/prog/



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

* Re: Constructing an object
  2005-09-21 11:55 ` Dmitry A. Kazakov
@ 2005-09-22  7:28   ` Maciej Sobczak
  2005-09-22  7:45     ` Maciej Sobczak
  0 siblings, 1 reply; 34+ messages in thread
From: Maciej Sobczak @ 2005-09-22  7:28 UTC (permalink / raw)


Dmitry A. Kazakov wrote:

>>I'm learning Ada and I would like to better understand the ways used to 
>>create objects.

>>1. By procedure, which is a primitive operation for each type in the 
>>hierarchy,

>>2. By function:


> 3. The default values:

OK, this solves some of the cases. In general, however, by 
"initialization" I mean performing some actions (not just inventing 
values), like acquire on external resources.

> 4. Initialization enforcement via discriminants:
> 
> 4.a. Discriminants as members:
> 
> type Shape (C : Color) is tagged null record;
>    -- C has to be defined

I think it has the same problem.

> 4.b. Box-discriminants:
> 
>    type Shape (<>) is tagged private;
>         -- Shape objects have to be initialized
> 
> private
>    type Shape is tagged record
>       C : Color;
>    end record;

I will have to read more about this, thanks for pointing the direction.

> 5. Abstract types with private descendants and a factory:
> 
>    type Abstract_Shape is abstract tagged null record;
>    function New_Shape (...) return Abstract_Shape'Class;
>       -- Factory is the only public way to create it
> private
>    type Shape is new Abstract_Shape with ...;

And I think this is the most general solution. In addition, the types 
that I'm crazy about initializing are those which can introduce a lot of 
problems when not well encapsulated. For the same reason it makes sense 
to make them private and carefully design their behavioral interface. 
The above scheme should work well for them.

Thanks for response,

-- 
Maciej Sobczak : http://www.msobczak.com/
Programming    : http://www.msobczak.com/prog/



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

* Re: Constructing an object
  2005-09-22  7:28   ` Maciej Sobczak
@ 2005-09-22  7:45     ` Maciej Sobczak
  2005-09-22 13:33       ` Dmitry A. Kazakov
  0 siblings, 1 reply; 34+ messages in thread
From: Maciej Sobczak @ 2005-09-22  7:45 UTC (permalink / raw)


Maciej Sobczak wrote:

>> 3. The default values:
> 
> OK, this solves some of the cases. In general, however, by 
> "initialization" I mean performing some actions (not just inventing 
> values), like acquire on external resources.

And I just realized that I don't have to use constants as default 
values, thay can come from functions...

>> 4. Initialization enforcement via discriminants:
>>
>> 4.a. Discriminants as members:

... and here those functions could even take the discriminant as a 
parameter:


function Get_New(Param : in SomeType) returns OtherType is
begin
     -- ...
end Get_Value;

type Record (Param : SomeType) is record
     X : OtherType := Get_New(Param);
end record;

A : Record(7);

This way, I can execute some action in order to initialize the object, 
and even use some parameters.

Further questions: Are there any restrictions on SomeType?


-- 
Maciej Sobczak : http://www.msobczak.com/
Programming    : http://www.msobczak.com/prog/



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

* Re: Constructing an object
  2005-09-22  7:45     ` Maciej Sobczak
@ 2005-09-22 13:33       ` Dmitry A. Kazakov
  2005-09-24  5:23         ` Randy Brukardt
  0 siblings, 1 reply; 34+ messages in thread
From: Dmitry A. Kazakov @ 2005-09-22 13:33 UTC (permalink / raw)


On Thu, 22 Sep 2005 09:45:24 +0200, Maciej Sobczak wrote:

> function Get_New(Param : in SomeType) returns OtherType is
> begin
>      -- ...
> end Get_Value;
> 
> type Record (Param : SomeType) is record
>      X : OtherType := Get_New(Param);
> end record;
> 
> A : Record(7);
> 
> This way, I can execute some action in order to initialize the object, 
> and even use some parameters.
> 
> Further questions: Are there any restrictions on SomeType?

Yes. It must be either a discrete or access type. In the latter case the
discriminated type has to be limited.

So there a sufficient limitations on what can be hidden behind X : T(Y);

-- 
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de



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

* Re: Constructing an object
  2005-09-21  8:46 Constructing an object Maciej Sobczak
  2005-09-21 10:16 ` Georg Bauhaus
  2005-09-21 11:55 ` Dmitry A. Kazakov
@ 2005-09-23  5:40 ` Matthew Heaney
  2005-09-23  7:18   ` tmoran
  2005-09-23  8:23   ` Maciej Sobczak
  2 siblings, 2 replies; 34+ messages in thread
From: Matthew Heaney @ 2005-09-23  5:40 UTC (permalink / raw)


Maciej Sobczak <no.spam@no.spam.com> writes:

> Let's say I have the following:
> 
>     type Color is (Black, Red, Green, Blue, White);
> 
>     type Shape is tagged record
>        C : Color;
>     end record;
> 
>     type Rectangle is new Shape with record
>        A, B : Positive;
>     end record;


If you want greater control of object creation and initialization, then
you could make the types private:

package Shapes is
   type Color is ...;
   type Shape is tagged private;
   type Rectangle is new Shape with private;

   function New_Shape (C : Color) return Shape;
   function New_Rectangle (C : Color) return Rectangle;
   -- other ctors with dif't params as desired
private
   type Shape is tagged record ...;
   type Rectangle is new Shape with record ...;
end Shapes;


> 1. By procedure, which is a primitive operation for each type in the
> hierarchy, for example:

Use a function.  In Ada 2005, you can use a function even for limited types.


> 2. By function:
> 
>     function New_Shape(C : in Color) return Shape is
>        S : Shape;
>     begin
>        S.C := C;
>        return S;
>     end;

Yes, use a function.  However (and I'm really thinking of controlled
types here) you might want to use aggregate syntax:

   function New_Shape (C : Color) return Shape is
   begin
      return Shape'(C => C);
   end;

In Ada 2005, you'll be able say (I don't know if my syntax is quite
right):

   function New_Shape (C : Color) return Shape is
   begin
     return S : Shape do
        S.C := C;
     end return;
   end;


>   function New_Rectangle(A, B : in Positive; C : in Color) return
> Rectangle is
>        R : Rectangle;
>     begin
>        Shape(R) := New_Shape(C);   -- is this OK?

I think this is legal; it's called a "view conversion."


>        R.A := A;
>        R.B := B;
>        return R;
>     end;
> 
> This does not please me for the same reason - no enforcement of proper
> initialization, if a given type makes no sense uninitialized.

But that's because you didn't declare the type in such a way that
initialization is enforced.  You have a few possibilities, including
declaring the type as private, declaring it as private and with an
unknown discriminant, declaring it with a known discriminant, or giving
the record components default values.  


> What approach do you actually use in the real (non-tutorial) code?
> Are there other techniques?
> (I'm aware of the controlled types, but I need to provide parameters for
> construction, which the special Initialize procedure does not have.)

Not quite.  You can combine a function as above, but make the type
controlled.  Something like:

package Shapes is
   type Shape is tagged private;
   function New_Shape (C : Color) return Shape;
private
   type Shape is new Controlled with record
      C : Color;
   end record;

   procedure Initialize (S : in out Shape);
end;


package body Shapes is
  function New_Shape (C : Color) return Shape is
  begin
     return Shape'(Controlled with C => C);
  end;




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

* Re: Constructing an object
  2005-09-23  5:40 ` Matthew Heaney
@ 2005-09-23  7:18   ` tmoran
  2005-09-23  8:23   ` Maciej Sobczak
  1 sibling, 0 replies; 34+ messages in thread
From: tmoran @ 2005-09-23  7:18 UTC (permalink / raw)


>> (I'm aware of the controlled types, but I need to provide parameters for
>> construction, which the special Initialize procedure does not have.)
  Access discriminants let you pass just about anything to the initialize
procedure.  I personally like a Cursor type taking an access discriminant
on the object it's supposed to traverse.  That not only lets the Cursor's
Initialize get needed information, it also enforces a lifetime for the
cursor that's within the lifetime of the traversed object.



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

* Re: Constructing an object
  2005-09-23  5:40 ` Matthew Heaney
  2005-09-23  7:18   ` tmoran
@ 2005-09-23  8:23   ` Maciej Sobczak
  2005-09-23 12:04     ` Dmitry A. Kazakov
  2005-09-23 12:24     ` Matthew Heaney
  1 sibling, 2 replies; 34+ messages in thread
From: Maciej Sobczak @ 2005-09-23  8:23 UTC (permalink / raw)


Hi,

Matthew Heaney wrote:

> But that's because you didn't declare the type in such a way that
> initialization is enforced.  You have a few possibilities, including
> declaring the type as private,

Is it really enough? Consider this:

-- file shapes.ads
package Shapes is
    type Color is (Black, White);
    type Shape is private;   -- or tagged private
    function New_Shape(C : in Color) return Shape;
private
    type Shape is record     -- or tagged
       C : Color;
    end record;
end Shapes;

-- file shapes.adb
package body Shapes is
    function New_Shape(C : in Color) return Shape is
    begin
       return Shape'(C => C);  -- I like this syntax :)
    end New_Shape;
end Shapes;

-- file hello.adb
with Shapes;
procedure Hello is
    S : Shapes.Shape;  -- oops, uninitialized Shape
begin
    null;
end Hello;


It compiles fine (GNAT) and it allows me to declare an uninitialized 
object of private type Shape. The only thing that private type gives me 
is that I cannot tinker with its internals directly. This is good in 
itself, but not enough to save me from having uninitialized objects.

I think that the most general approach is with abstract types, although 
at the same time it looks like an overkill in simpler cases and not 
without its own problems. In particular, it introduces an artifical 
hierarchy of types (not counting the cases where the hierarchy already 
exists at the design time, like with true Shapes, Animals, etc.).

I miss constructors. The "real" ones. ;-)
(no flame intended)


> declaring it as private and with an
> unknown discriminant, declaring it with a known discriminant, or giving
> the record components default values.


-- 
Maciej Sobczak : http://www.msobczak.com/
Programming    : http://www.msobczak.com/prog/



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

* Re: Constructing an object
  2005-09-23  8:23   ` Maciej Sobczak
@ 2005-09-23 12:04     ` Dmitry A. Kazakov
  2005-09-23 12:36       ` Matthew Heaney
  2005-09-23 12:24     ` Matthew Heaney
  1 sibling, 1 reply; 34+ messages in thread
From: Dmitry A. Kazakov @ 2005-09-23 12:04 UTC (permalink / raw)


On Fri, 23 Sep 2005 10:23:02 +0200, Maciej Sobczak wrote:

> Is it really enough? Consider this:
> 
> -- file shapes.ads
> package Shapes is
>     type Color is (Black, White);
>     type Shape is private;   -- or tagged private

Replace it with:

type Shape (<>) is private;

BTW, private vs. tagged private is a huge difference.

>     function New_Shape(C : in Color) return Shape;
> private
>     type Shape is record     -- or tagged
>        C : Color;
>     end record;
> end Shapes;
> 
> -- file shapes.adb
> package body Shapes is
>     function New_Shape(C : in Color) return Shape is
>     begin
>        return Shape'(C => C);  -- I like this syntax :)
>     end New_Shape;
> end Shapes;
> 
> -- file hello.adb
> with Shapes;
> procedure Hello is
>     S : Shapes.Shape;  -- oops, uninitialized Shape
> begin
>     null;
> end Hello;
> 
> It compiles fine (GNAT) and it allows me to declare an uninitialized 
> object of private type Shape. The only thing that private type gives me 
> is that I cannot tinker with its internals directly. This is good in 
> itself, but not enough to save me from having uninitialized objects.

It is enough if the public view is made up so that objects look
unconstrained. This is what (<>) does.

-- 
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de



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

* Re: Constructing an object
  2005-09-23  8:23   ` Maciej Sobczak
  2005-09-23 12:04     ` Dmitry A. Kazakov
@ 2005-09-23 12:24     ` Matthew Heaney
  2005-09-24  5:34       ` Randy Brukardt
  1 sibling, 1 reply; 34+ messages in thread
From: Matthew Heaney @ 2005-09-23 12:24 UTC (permalink / raw)


Maciej Sobczak <no.spam@no.spam.com> writes:

> Is it really enough? Consider this:
>
> with Shapes;
> procedure Hello is
>     S : Shapes.Shape;  -- oops, uninitialized Shape
> begin
>     null;
> end Hello;

I should have been more specific.  In addition to declaring the type as
private, you have to either provide a default for the C component of the
Shape record, or privately derive from Controlled and assign C a value
in the Initialize operation.


> It compiles fine (GNAT) and it allows me to declare an uninitialized
> object of private type Shape. The only thing that private type gives me
> is that I cannot tinker with its internals directly. This is good in
> itself, but not enough to save me from having uninitialized objects.

Right.


> I think that the most general approach is with abstract types, 

You don't need abstract types for this problem.  You would only need to
declare the type abstract if you were declaring an interface (indeed, in
Ada 2005 there's even a special keyword for that), for which you're
unable to provide an implemention for some of the operations.


> although
> at the same time it looks like an overkill in simpler cases and not
> without its own problems. In particular, it introduces an artifical
> hierarchy of types (not counting the cases where the hierarchy already
> exists at the design time, like with true Shapes, Animals, etc.).

Right.  In this case, you don't need an abstract type to be the root of
your type hierarchy.


> I miss constructors. The "real" ones. ;-) (no flame intended)

Well, this issue has been discussed for a long time.  In Ada you use a
function for this purpose.  The only snag is if this is a tagged type,
then you have to treat value-returning functions with care.  You
probably want to make the function non-primitive.  (Note that this is an
error in my last post.)  That means either wrap the value-returning
functions in a nested package (or possibly as children), or return a
class-wide type.

The big improvement in Ada 2005 is that there's new syntax for
initializing a return value (I showed it in one of my examles), that you
can use even for limited types.  (Only for limited types?  Hopefully an
ARG member will jump in here and, er, elaborate.)



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

* Re: Constructing an object
  2005-09-23 12:04     ` Dmitry A. Kazakov
@ 2005-09-23 12:36       ` Matthew Heaney
  2005-09-23 13:03         ` Hyman Rosen
  2005-09-23 13:42         ` Dmitry A. Kazakov
  0 siblings, 2 replies; 34+ messages in thread
From: Matthew Heaney @ 2005-09-23 12:36 UTC (permalink / raw)


"Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> writes:

> Replace it with:
> 
> type Shape (<>) is private;

Yes, but now the type is indefinite, which means you can't declare it as
a component of a composite type.  (This is no different from C++, when
you hide the default ctor.)

The easiest thing is just to default initialize the component(s), and
don't bother declaring the type as indefinite. If you still want to
force some post-declaration initialization, then you do something like:

  type Color_Base is (Uninitialized, Red, Black);
  subtype Color is Color_Base range Red .. Black;

  type Shape is tagged private;

  procedure Set_Color (S : in out Shape; C : Color);
...
private
  type Shape is tagged record
     C : Color_Base := Uninitialized;
  end record;
...
end Shapes;

But again, that would only be necessary if you don't have a sensible
default.  In this particular case, you probably don't need to bother;
just pick on of the colors as the default.



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

* Re: Constructing an object
  2005-09-23 12:36       ` Matthew Heaney
@ 2005-09-23 13:03         ` Hyman Rosen
  2005-09-23 13:41           ` Maciej Sobczak
  2005-09-23 14:23           ` Matthew Heaney
  2005-09-23 13:42         ` Dmitry A. Kazakov
  1 sibling, 2 replies; 34+ messages in thread
From: Hyman Rosen @ 2005-09-23 13:03 UTC (permalink / raw)


Matthew Heaney wrote:
> Yes, but now the type is indefinite, which means you can't declare it as
> a component of a composite type.  (This is no different from C++, when
> you hide the default ctor.)

Just to correct this, C++ does not require a default constructor
for objects which are components of classes, but does require them
for objects which are elements of arrays.




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

* Re: Constructing an object
  2005-09-23 13:03         ` Hyman Rosen
@ 2005-09-23 13:41           ` Maciej Sobczak
  2005-09-23 14:23           ` Matthew Heaney
  1 sibling, 0 replies; 34+ messages in thread
From: Maciej Sobczak @ 2005-09-23 13:41 UTC (permalink / raw)


Hyman Rosen wrote:

> Just to correct this, C++ does not require a default constructor
> for objects which are components of classes, but does require them
> for objects which are elements of arrays.

Even with arrays it is not always the case:

class A
{
public:
      A(int) { /* ... */ }
private:
      A();  // default ctor not available
};

int main()
{
      A array[] = {1, 2, 3, 4}; // OK
}


-- 
Maciej Sobczak : http://www.msobczak.com/
Programming    : http://www.msobczak.com/prog/



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

* Re: Constructing an object
  2005-09-23 12:36       ` Matthew Heaney
  2005-09-23 13:03         ` Hyman Rosen
@ 2005-09-23 13:42         ` Dmitry A. Kazakov
  2005-09-23 14:27           ` Matthew Heaney
  1 sibling, 1 reply; 34+ messages in thread
From: Dmitry A. Kazakov @ 2005-09-23 13:42 UTC (permalink / raw)


On Fri, 23 Sep 2005 12:36:05 GMT, Matthew Heaney wrote:

> "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> writes:
> 
>> Replace it with:
>> 
>> type Shape (<>) is private;
> 
> Yes, but now the type is indefinite, which means you can't declare it as
> a component of a composite type.

You mean publicly:

type Foo is record
   X : Shape;
end record;

because privately it will be OK:

   type Foo is private;
private
   type Foo is record
      X : Shape;
   end record;

Which is quite logical in my view.

> The easiest thing is just to default initialize the component(s), and
> don't bother declaring the type as indefinite. If you still want to
> force some post-declaration initialization,

Yes, when possible.

-- 
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de



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

* Re: Constructing an object
  2005-09-23 13:03         ` Hyman Rosen
  2005-09-23 13:41           ` Maciej Sobczak
@ 2005-09-23 14:23           ` Matthew Heaney
  2006-01-17  6:28             ` [Offtopic] " James Dennett
  1 sibling, 1 reply; 34+ messages in thread
From: Matthew Heaney @ 2005-09-23 14:23 UTC (permalink / raw)



Hyman Rosen wrote:
> Just to correct this, C++ does not require a default constructor
> for objects which are components of classes, but does require them
> for objects which are elements of arrays.

Right.  I was really thinking of (not being able to use) the class as a
container element when I wrote that.




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

* Re: Constructing an object
  2005-09-23 13:42         ` Dmitry A. Kazakov
@ 2005-09-23 14:27           ` Matthew Heaney
  0 siblings, 0 replies; 34+ messages in thread
From: Matthew Heaney @ 2005-09-23 14:27 UTC (permalink / raw)



Dmitry A. Kazakov wrote:
> On Fri, 23 Sep 2005 12:36:05 GMT, Matthew Heaney wrote:
>
> because privately it will be OK:

Not quite.  Only if the full view of the type (here, Shape) is visible.




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

* Re: Constructing an object
  2005-09-22 13:33       ` Dmitry A. Kazakov
@ 2005-09-24  5:23         ` Randy Brukardt
  2005-09-24  9:47           ` Dmitry A. Kazakov
  0 siblings, 1 reply; 34+ messages in thread
From: Randy Brukardt @ 2005-09-24  5:23 UTC (permalink / raw)


"Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message
news:f1idk7ptk100.onel9bzfn1nr$.dlg@40tude.net...
...
> Yes. It must be either a discrete or access type. In the latter case the
> discriminated type has to be limited.

...in Ada 95. In Ada 200Y, access discriminants are allowed on nonlimited
types, but they then cannot have defaults (such discriminants cannot change
after object creation, and we needed to disallow changing them via
assignment).

You also failed to mention that the object factory can be implemented
(probably *ought* to be implemented) with the Ada 200Y
Generic_Dispatching_Constructor. That takes a tag value and a dispatching
function and creates an appropriate object for the tag, initialized by the
dispatching function. While its primary purpose was to make it possible to
implement user-defined T'Class'Input functions, it can be useful in other
circumstances as well (such as construction from a menu).

                       Randy.







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

* Re: Constructing an object
  2005-09-23 12:24     ` Matthew Heaney
@ 2005-09-24  5:34       ` Randy Brukardt
  0 siblings, 0 replies; 34+ messages in thread
From: Randy Brukardt @ 2005-09-24  5:34 UTC (permalink / raw)


"Matthew Heaney" <matthewjheaney@earthlink.net> wrote in message
news:ud5n0t1jv.fsf@earthlink.net...
> The big improvement in Ada 2005 is that there's new syntax for
> initializing a return value (I showed it in one of my examles), that you
> can use even for limited types.  (Only for limited types?  Hopefully an
> ARG member will jump in here and, er, elaborate.)

You were right the first time; any function can use the extended return
statement. Only limited types have a guarantee of building the result in
place, but compilers can do that for any object when they can arrange it.

The *other* big improvement in Ada 200Y is the
generic_dispatching_constructor I mentioned earlier, since it lets you
create objects of a *type* determined at runtime (not just a *subtype*). Ada
95 has that in the predefined T'Class'Input streaming function, but there
was no way for the user to write their own. That allows you to avoid giant
case statements that include every type in the program; instead you can use
a registration scheme so that adding a type to the program is as easy as
withing the package for the new type.

                               Randy.






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

* Re: Constructing an object
  2005-09-24  5:23         ` Randy Brukardt
@ 2005-09-24  9:47           ` Dmitry A. Kazakov
  2005-09-29  0:12             ` Randy Brukardt
  0 siblings, 1 reply; 34+ messages in thread
From: Dmitry A. Kazakov @ 2005-09-24  9:47 UTC (permalink / raw)


On Sat, 24 Sep 2005 00:23:01 -0500, Randy Brukardt wrote:

> In Ada 200Y, access discriminants are allowed on nonlimited
> types, but they then cannot have defaults (such discriminants cannot change
> after object creation, and we needed to disallow changing them via
> assignment).

How? The type should be then abstract or unconstrained and the user should
have rather its subtype with the discriminant limited to the value.

> You also failed to mention that the object factory can be implemented
> (probably *ought* to be implemented) with the Ada 200Y
> Generic_Dispatching_Constructor. That takes a tag value and a dispatching
> function and creates an appropriate object for the tag, initialized by the
> dispatching function.

That is a good news, though it still looks like hack. Generics is used to
pass the parameters, instead of providing some native mechanism for
determining the tag, discriminants, bounds and other constraints. One
possibility could be to have for each type a predefined null-record type
having the same discriminants (tag and bounds are mapped to discriminants).
Then the object of this type could be passed to the allocator.

IMO there should be proper constructors for all types. Constructing
functions are too error-prone. Further, Ada needs extensible primitive and
class-wide operations, that could finally solve the construction /
assignment problem.

-- 
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de



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

* Re: Constructing an object
  2005-09-24  9:47           ` Dmitry A. Kazakov
@ 2005-09-29  0:12             ` Randy Brukardt
  2005-09-29  8:17               ` Dmitry A. Kazakov
  0 siblings, 1 reply; 34+ messages in thread
From: Randy Brukardt @ 2005-09-29  0:12 UTC (permalink / raw)


"Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message
news:3dachnu33lly.fpr0wvf6nj05$.dlg@40tude.net...
> On Sat, 24 Sep 2005 00:23:01 -0500, Randy Brukardt wrote:
>
> > In Ada 200Y, access discriminants are allowed on nonlimited
> > types, but they then cannot have defaults (such discriminants cannot
change
> > after object creation, and we needed to disallow changing them via
> > assignment).
>
> How? The type should be then abstract or unconstrained and the user should
> have rather its subtype with the discriminant limited to the value.

If the discriminants don't have defaults, the type *is* unconstrained. The
user has to provide the discriminant value with the object declaration or
initial value, or from a subtype declaration that is then used to create
object.

> > You also failed to mention that the object factory can be implemented
> > (probably *ought* to be implemented) with the Ada 200Y
> > Generic_Dispatching_Constructor. That takes a tag value and a
dispatching
> > function and creates an appropriate object for the tag, initialized by
the
> > dispatching function.
>
> That is a good news, though it still looks like hack.

It *is* a hack! A hack is better than the Ada 95 situation (no way to do
it). The whole idea is simply to provide a way to make the mechanism that
compilers have visible to the user.

> Generics is used to
> pass the parameters, instead of providing some native mechanism for
> determining the tag, discriminants, bounds and other constraints.

We tried a lot of those, but they all fail because Ada allows discriminants
on tagged types. That makes it necessary for the user code to create the
object; doing the operations separately is not an option. The only thing
that I know *would* work (new syntax to allow dispatching on a provided tag
value) was considered way too heavyweight for a relatively minor problem.

> One
> possibility could be to have for each type a predefined null-record type
> having the same discriminants (tag and bounds are mapped to
discriminants).
> Then the object of this type could be passed to the allocator.

What allocator? The critical part of the whole scheme is that there is no
allocator required; this works just as well on stack objects as it does on
heap-allocated objects. It should never be necessary to use heap-allocated
objects (or syntax like 'Access and .all) to get the benefits of O-O design.

> IMO there should be proper constructors for all types. Constructing
> functions are too error-prone.

No one agreed with that. The only problem with using functions for
constructors was the weird semantics for limited types, which was solved by
getting rid of those semantics. If you want to ensure that constructor
functions are used, you need to declare your types with (<>) discriminants.

> Further, Ada needs extensible primitive and
> class-wide operations, that could finally solve the construction /
> assignment problem.

I have no idea what this means; Ada has extensible primitive operations
(indeed, *only* primitive operations can be extended).

                       Randy.







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

* Re: Constructing an object
  2005-09-29  0:12             ` Randy Brukardt
@ 2005-09-29  8:17               ` Dmitry A. Kazakov
  2005-09-29 22:21                 ` Randy Brukardt
  0 siblings, 1 reply; 34+ messages in thread
From: Dmitry A. Kazakov @ 2005-09-29  8:17 UTC (permalink / raw)


On Wed, 28 Sep 2005 19:12:40 -0500, Randy Brukardt wrote:

> "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message
> news:3dachnu33lly.fpr0wvf6nj05$.dlg@40tude.net...
>> On Sat, 24 Sep 2005 00:23:01 -0500, Randy Brukardt wrote:
>>
>>> In Ada 200Y, access discriminants are allowed on nonlimited
>>> types, but they then cannot have defaults (such discriminants cannot change
>>> after object creation, and we needed to disallow changing them via
>>> assignment).
>>
>> How? The type should be then abstract or unconstrained and the user should
>> have rather its subtype with the discriminant limited to the value.
> 
> If the discriminants don't have defaults, the type *is* unconstrained. The
> user has to provide the discriminant value with the object declaration or
> initial value, or from a subtype declaration that is then used to create
> object.

I see, it is same trick as with String.

>> Generics is used to
>> pass the parameters, instead of providing some native mechanism for
>> determining the tag, discriminants, bounds and other constraints.
> 
> We tried a lot of those, but they all fail because Ada allows discriminants
> on tagged types. That makes it necessary for the user code to create the
> object; doing the operations separately is not an option. The only thing
> that I know *would* work (new syntax to allow dispatching on a provided tag
> value) was considered way too heavyweight for a relatively minor problem.
> 
>> One
>> possibility could be to have for each type a predefined null-record type
>> having the same discriminants (tag and bounds are mapped to discriminants).
>> Then the object of this type could be passed to the allocator.
> 
> What allocator? The critical part of the whole scheme is that there is no
> allocator required; this works just as well on stack objects as it does on
> heap-allocated objects. It should never be necessary to use heap-allocated
> objects (or syntax like 'Access and .all) to get the benefits of O-O design.

Sure. But it is just a stack allocator in my view. The idea is that when
you write:

   type T (<constraints>) is ...;
   ...
   X : T (<parameters>);

The compiler would first call function:

   function T'Get_Constraints (<parameters>) return T'Constraints;
   
And T'Constraints is a pre-defined null record type:

   type T'Constraints (<constraints>) is null record;

The user can declare Get_Constraints, or override a predefined one. The
latter happens when the discriminants are visible, so that
<constraints>=<parameters>. The function will evaluate discriminants, array
bounds, the tag (if T is a class-wide type), and put them into that null
record. For array types, the record could contain First, Last components:

   type String'Constraints is record
      First : Positive;
      Last : Positive;
   end record;

This way, I believe, we could have constructors with arbitrary parameters
for all types.

>> IMO there should be proper constructors for all types. Constructing
>> functions are too error-prone.
> 
> No one agreed with that. The only problem with using functions for
> constructors was the weird semantics for limited types, which was solved by
> getting rid of those semantics. If you want to ensure that constructor
> functions are used, you need to declare your types with (<>) discriminants.

That is a minor problem in my view. The real one appears in OO design,
because a function can be overridden only as a whole.

>> Further, Ada needs extensible primitive and
>> class-wide operations, that could finally solve the construction /
>> assignment problem.
> 
> I have no idea what this means; Ada has extensible primitive operations
> (indeed, *only* primitive operations can be extended).

They aren't extensible in the sense that you can only override or inherit.
It is too dangerous for constructors and assignments. BTW, Ada does not to
completely override them anyway. For example the parts responsible for
initialization of the components cannot be overridden. One cannot have a
task component which will not start because Initialize does not call the
parent's one! (:-))

There should be a mechanism for extension of an inherited body available
for user-defined primitive subprograms. It could be extremely useful not
only for constructors, but also for task entries, protected operations,
once they become primitive.

-- 
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de



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

* Re: Constructing an object
  2005-09-29  8:17               ` Dmitry A. Kazakov
@ 2005-09-29 22:21                 ` Randy Brukardt
  2005-09-30  8:14                   ` Jean-Pierre Rosen
  2005-09-30 17:49                   ` Robert A Duff
  0 siblings, 2 replies; 34+ messages in thread
From: Randy Brukardt @ 2005-09-29 22:21 UTC (permalink / raw)


"Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message
news:88m4nn9wh78b$.1wtlfwjymgcom.dlg@40tude.net...
> On Wed, 28 Sep 2005 19:12:40 -0500, Randy Brukardt wrote:
...
> This way, I believe, we could have constructors with arbitrary parameters
> for all types.

Interesting. I don't think that would work for Ada as it is currently
defined, because we tried to do something like that in the implementation of
the Janus/Ada compiler and it didn't work. I believe the problems had to do
with composition (that is, calling the functions for the inner components of
a record whose constraints depend on the outer constraints). But I don't
remember all of the details.
...
> >> Further, Ada needs extensible primitive and
> >> class-wide operations, that could finally solve the construction /
> >> assignment problem.
> >
> > I have no idea what this means; Ada has extensible primitive operations
> > (indeed, *only* primitive operations can be extended).
>
> They aren't extensible in the sense that you can only override or inherit.
> It is too dangerous for constructors and assignments. BTW, Ada does not to
> completely override them anyway. For example the parts responsible for
> initialization of the components cannot be overridden. One cannot have a
> task component which will not start because Initialize does not call the
> parent's one! (:-))

Ah, I see. Certainly you are right here. I see that a lot in Finalize
routines; it is real easy to forget to call the Finalize for the parent
type. There ought to be a better way of extending rather than replacing
routines; that's especially true since requires an explicit type conversion
to the parent to make that call, and it is easy to get wrong (and go
infinitely recursive).

No one proposed anything that would work, though. My syntax proposal (which
predated the current generic_dispatching_constructor) tried to include that
functionality, but it would have been a band-aid at best. This issue was
never as high on the radar as others (like the user-defined T'Class'Input
problem), so it didn't get handled.

                                  Randy.






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

* Re: Constructing an object
  2005-09-29 22:21                 ` Randy Brukardt
@ 2005-09-30  8:14                   ` Jean-Pierre Rosen
  2005-09-30 19:28                     ` Dmitry A. Kazakov
  2005-09-30 17:49                   ` Robert A Duff
  1 sibling, 1 reply; 34+ messages in thread
From: Jean-Pierre Rosen @ 2005-09-30  8:14 UTC (permalink / raw)


Randy Brukardt a �crit :
> "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message
>>They aren't extensible in the sense that you can only override or inherit.
>>It is too dangerous for constructors and assignments. BTW, Ada does not to
>>completely override them anyway. For example the parts responsible for
>>initialization of the components cannot be overridden. One cannot have a
>>task component which will not start because Initialize does not call the
>>parent's one! (:-))
> 
> 
> Ah, I see. Certainly you are right here. I see that a lot in Finalize
> routines; it is real easy to forget to call the Finalize for the parent
> type. There ought to be a better way of extending rather than replacing
> routines; that's especially true since requires an explicit type conversion
> to the parent to make that call, and it is easy to get wrong (and go
> infinitely recursive).
> 
I think CLOS has such a feature, but it raises an issue:
sometimes you want to call the parent Finalize *before* your own 
Finalize, sometimes *after*. If it is automatic, you must force one of 
them, which makes problems if you want the other one.

-- 
---------------------------------------------------------
            J-P. Rosen (rosen@adalog.fr)
Visit Adalog's web site at http://www.adalog.fr



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

* Re: Constructing an object
  2005-09-29 22:21                 ` Randy Brukardt
  2005-09-30  8:14                   ` Jean-Pierre Rosen
@ 2005-09-30 17:49                   ` Robert A Duff
  2005-10-01  0:44                     ` Randy Brukardt
  1 sibling, 1 reply; 34+ messages in thread
From: Robert A Duff @ 2005-09-30 17:49 UTC (permalink / raw)


"Randy Brukardt" <randy@rrsoftware.com> writes:

> Ah, I see. Certainly you are right here. I see that a lot in Finalize
> routines; it is real easy to forget to call the Finalize for the parent
> type. There ought to be a better way of extending rather than replacing
> routines; that's especially true since requires an explicit type conversion
> to the parent to make that call, and it is easy to get wrong (and go
> infinitely recursive).

The Common Lisp Object System has such a feature.  You can declare
methods that are called in addition to the parent's version (either
before or after) rather than the more common feature of calling them
instead of the parent's version.  And I think there's a feature for
declaring a method that must call the parent's version somewhere within
it -- or something like that.

If such additional complexity were to be added to Ada (I doubt it!),
it would be a shame to make it work just for Initialize/Finalize.
It really should be a general purpose feature.  This need (where an
overriding "should" call the parent) comes up a lot in OO code;
Finalize is just a special case.

- Bob



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

* Re: Constructing an object
  2005-09-30  8:14                   ` Jean-Pierre Rosen
@ 2005-09-30 19:28                     ` Dmitry A. Kazakov
  0 siblings, 0 replies; 34+ messages in thread
From: Dmitry A. Kazakov @ 2005-09-30 19:28 UTC (permalink / raw)


On Fri, 30 Sep 2005 10:14:47 +0200, Jean-Pierre Rosen wrote:

> Randy Brukardt a �crit :
>> "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message
>>>They aren't extensible in the sense that you can only override or inherit.
>>>It is too dangerous for constructors and assignments. BTW, Ada does not to
>>>completely override them anyway. For example the parts responsible for
>>>initialization of the components cannot be overridden. One cannot have a
>>>task component which will not start because Initialize does not call the
>>>parent's one! (:-))
>> 
>> Ah, I see. Certainly you are right here. I see that a lot in Finalize
>> routines; it is real easy to forget to call the Finalize for the parent
>> type. There ought to be a better way of extending rather than replacing
>> routines; that's especially true since requires an explicit type conversion
>> to the parent to make that call, and it is easy to get wrong (and go
>> infinitely recursive).
>> 
> I think CLOS has such a feature, but it raises an issue:
> sometimes you want to call the parent Finalize *before* your own 
> Finalize, sometimes *after*. If it is automatic, you must force one of 
> them, which makes problems if you want the other one.

Yes, but I almost sure that this one is rooted in an incompleteness of the
construction model. If we consider T and T'Class as different types, then
we should consistently conclude that they might have constructors of their
own. (Of course T'Class destructor includes one of T.) I wonder if all
cases when one needs to call parent's Finalize out of order, are actually
ones which should be handled by the Finalize of T'Class. Now because
class-wide objects should be finalized before all specific ones, Finalize
of T'Class would be called before Finalize of any S derived from T. One
could even safely dispatch from it!

-- 
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de



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

* Re: Constructing an object
  2005-09-30 17:49                   ` Robert A Duff
@ 2005-10-01  0:44                     ` Randy Brukardt
  2005-10-01 10:49                       ` Dmitry A. Kazakov
                                         ` (2 more replies)
  0 siblings, 3 replies; 34+ messages in thread
From: Randy Brukardt @ 2005-10-01  0:44 UTC (permalink / raw)


"Robert A Duff" <bobduff@shell01.TheWorld.com> wrote in message
news:wccirwi30q7.fsf@shell01.TheWorld.com...
...
> The Common Lisp Object System has such a feature.  You can declare
> methods that are called in addition to the parent's version (either
> before or after) rather than the more common feature of calling them
> instead of the parent's version.  And I think there's a feature for
> declaring a method that must call the parent's version somewhere within
> it -- or something like that.
>
> If such additional complexity were to be added to Ada (I doubt it!),
> it would be a shame to make it work just for Initialize/Finalize.
> It really should be a general purpose feature.  This need (where an
> overriding "should" call the parent) comes up a lot in OO code;
> Finalize is just a special case.

Yes, of course it should work everywhere. Finalize routines were just an
example of a routine that needs it.

Actually, it wouldn't be that hard to add that to Ada 200Y. We have the
"overriding" keyword to specify overriding. Additional keywords could
specify other types of extension -- "extends" perhaps. That would look like:

    extends
    procedure Finalize (Obj : in out My_Type);

or perhaps

    extends after
    procedure Finalize (Obj : in out My_Type);

to specify when the parent routine is called.

But I think that we'd only want to support calling the parent routine first
or last; special syntax for calling it in the middle hardly seems worth it
(and getting the parameters right then would be messy - calling the parent
last could be done with jumps, and first probably also could be done with
shared code).

I'm mildly sorry I didn't think of this when we were working on "overrides";
its a rather natural extension to the idea that also helps to prevent bugs.

                         Randy.






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

* Re: Constructing an object
  2005-10-01  0:44                     ` Randy Brukardt
@ 2005-10-01 10:49                       ` Dmitry A. Kazakov
  2005-10-01 11:06                       ` Tapio Kelloniemi
  2005-10-01 15:19                       ` Georg Bauhaus
  2 siblings, 0 replies; 34+ messages in thread
From: Dmitry A. Kazakov @ 2005-10-01 10:49 UTC (permalink / raw)


On Fri, 30 Sep 2005 19:44:22 -0500, Randy Brukardt wrote:

> Actually, it wouldn't be that hard to add that to Ada 200Y. We have the
> "overriding" keyword to specify overriding. Additional keywords could
> specify other types of extension -- "extends" perhaps. That would look like:
> 
>     extends
>     procedure Finalize (Obj : in out My_Type);
> 
> or perhaps
> 
>     extends after
>     procedure Finalize (Obj : in out My_Type);
> 
> to specify when the parent routine is called.

Here is an alternative schema, which is actually a frequently used pattern. 
One could automate it: let each class-wide subroutine F have 
language-defined primitive operations F'Prologue and F'Epilogue. So:

   type T is tagged ...;
   type Foo (Obj : in out T'Class); -- An extensible operation on the class

   type My_Type is new T with ...;
   overriding procedure Foo'Prologue (Obj : in out My_Type); 
   overriding procedure Foo'Epilogue (Obj : in out My_Type); 

(of course F'{Pro|Epi}logue cannot be called directly)

But there is also safety issue. At least in constructors and destructors 
the parent should be able to prevent overriding of either the prologue or 
epilogue.

-- 
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de



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

* Re: Constructing an object
  2005-10-01  0:44                     ` Randy Brukardt
  2005-10-01 10:49                       ` Dmitry A. Kazakov
@ 2005-10-01 11:06                       ` Tapio Kelloniemi
  2005-10-01 14:13                         ` Robert A Duff
  2005-10-01 15:19                       ` Georg Bauhaus
  2 siblings, 1 reply; 34+ messages in thread
From: Tapio Kelloniemi @ 2005-10-01 11:06 UTC (permalink / raw)


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain, Size: 2762 bytes --]

"Randy Brukardt" <randy@rrsoftware.com> wrote:
>"Robert A Duff" <bobduff@shell01.TheWorld.com> wrote in message
>news:wccirwi30q7.fsf@shell01.TheWorld.com...
>...
>> The Common Lisp Object System has such a feature.  You can declare
>> methods that are called in addition to the parent's version (either
>> before or after) rather than the more common feature of calling them
>> instead of the parent's version.  And I think there's a feature for
>> declaring a method that must call the parent's version somewhere within
>> it -- or something like that.
>Actually, it wouldn't be that hard to add that to Ada 200Y. We have the
>"overriding" keyword to specify overriding. Additional keywords could
>specify other types of extension -- "extends" perhaps. That would look like:
>
>    extends
>    procedure Finalize (Obj : in out My_Type);
>
>or perhaps
>
>    extends after
>    procedure Finalize (Obj : in out My_Type);
>
>to specify when the parent routine is called.

Or perhaps:
   extending
   procedure ...;
or:
   procedure ... is extending;

These would be more English-like and would be coherent with overriding.

>But I think that we'd only want to support calling the parent routine first
>or last; special syntax for calling it in the middle hardly seems worth it
>(and getting the parameters right then would be messy - calling the parent
>last could be done with jumps, and first probably also could be done with
>shared code).

Dylan solves this with a special method called next-method. This
calls the next appropriate method with the same parameters as current
method was caled, unless the caller provides new p�rameters to be used
instead. This also works in multiple inheritance environment. This could
be adopted into Ada in a form of a special statement
like: parent Method (Self, parameters);
Or like: Method (Self'Parent_View, parameters);
This form of call can be put anywhere in a function or procedure body and
the programmer may use different parameters in calling Parent than what
where given to it. This is very useful, since an overridden implementation
may accept values which are being rejected by its parent method. Compilers,
however, need to do some extra work in this approach since they should
try their best to detect, if the parent is not called. If this fails
and the parent method is not called at run time, Program_Error would
be raised. I don't think this is a big problem since in most cases missing
parent method call can be identified like missing return statement.

>I'm mildly sorry I didn't think of this when we were working on "overrides";
>its a rather natural extension to the idea that also helps to prevent bugs.

Perhaps in Ada2015 then, or perhaps all this has been obsoleted since then.

-- 
Tapio



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

* Re: Constructing an object
  2005-10-01 11:06                       ` Tapio Kelloniemi
@ 2005-10-01 14:13                         ` Robert A Duff
  2005-10-02 11:52                           ` Tapio Kelloniemi
  0 siblings, 1 reply; 34+ messages in thread
From: Robert A Duff @ 2005-10-01 14:13 UTC (permalink / raw)


Tapio Kelloniemi <invalid@localhost.localdomain.com> writes:

>...If this fails
> and the parent method is not called at run time, Program_Error would
> be raised. I don't think this is a big problem since in most cases missing
> parent method call can be identified like missing return statement.

The rules about return statements probably should be static:
Every path through the function must have a return statement
or a raise statement.

Instead, we have: There must be at least one return statement
(never mind that it might be bogus, or might be skipped).
Plus a run-time check.

The rule you propose about calling the parent could be static,
too: exactly one call on every path (not inside a loop).

- Bob



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

* Re: Constructing an object
  2005-10-01  0:44                     ` Randy Brukardt
  2005-10-01 10:49                       ` Dmitry A. Kazakov
  2005-10-01 11:06                       ` Tapio Kelloniemi
@ 2005-10-01 15:19                       ` Georg Bauhaus
  2 siblings, 0 replies; 34+ messages in thread
From: Georg Bauhaus @ 2005-10-01 15:19 UTC (permalink / raw)


Randy Brukardt wrote:

> Actually, it wouldn't be that hard to add that to Ada 200Y. We have the
> "overriding" keyword to specify overriding. Additional keywords could
> specify other types of extension -- "extends" perhaps. That would look like:
> 
>     extends
>     procedure Finalize (Obj : in out My_Type);
> 
> or perhaps
> 
>     extends after
>     procedure Finalize (Obj : in out My_Type);
> 
> to specify when the parent routine is called.


AFAICS, the programming language BETA, successor to Simula, has virtual
patterns of the general kind, extending in descendants.
(Everything in BETA is a pattern, "The abstraction mechanisms
include class, procedure, function, coroutine, process, exception and many
more, all unified into the ultimate abstraction mechanism: the pattern.")

Virtual patterns actually use the wording suggested in this thread.

http://www.daimi.au.dk/~beta/Manuals/latest/beta-intro/Introduction.html#H3_1

From
http://www.daimi.au.dk/~beta/Manuals/latest/beta-intro/Virtual.html :

"In fact a virtual pattern in BETA can only be extended and cannot be
completely redefined."

The word "inner" is used to denote a part of a superpattern that is
defined in subpatterns.

From the second URL:

A: (# V:< (# x:  do I1; inner; I2 #) #);
AA: A(# V::< (# y:  do I3; inner; I4#) #)

The pattern A has a virtual procedure attribute V. V has an attribute x
and its do-part contains the execution of I1; inner; I2.
The subpattern AA of A extends the definition of V. The extended
definition of V in AA corresponds to the following object-descriptor
(except for scope rules):

4.3 Combined descriptor

(# x: ; y:  do I1; I3; inner; I4; I2 #)



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

* Re: Constructing an object
  2005-10-01 14:13                         ` Robert A Duff
@ 2005-10-02 11:52                           ` Tapio Kelloniemi
  0 siblings, 0 replies; 34+ messages in thread
From: Tapio Kelloniemi @ 2005-10-02 11:52 UTC (permalink / raw)


Robert A Duff <bobduff@shell01.TheWorld.com> wrote:
>Tapio Kelloniemi <invalid@localhost.localdomain.com> writes:
>
>>...If this fails
>> and the parent method is not called at run time, Program_Error would
>> be raised. I don't think this is a big problem since in most cases missing
>> parent method call can be identified like missing return statement.
>
>The rules about return statements probably should be static:
>Every path through the function must have a return statement
>or a raise statement.
>
>Instead, we have: There must be at least one return statement
>(never mind that it might be bogus, or might be skipped).
>Plus a run-time check.
>
>The rule you propose about calling the parent could be static,
>too: exactly one call on every path (not inside a loop).

But the idea of the extended syntax (parent statement or attribute) is
to be as useful as possible and I would allow it to be used several
times per subprogram and even to call parent versions of other overridden
subprograms, even in subprograms not declared to be "extending".

Something like:

extending procedure Do_A (Object : Parent) is
begin Do_Something; end;

extending procedure Do_A (Object : Child) is
begin Do_Something_Else; Do_A (Object'Parent); end;

procedure Do_A_And_B (Object : Child) is
begin Do_Something_B; Object'Parent.Do_A; end;
--  The above is extending Do_A though it has no relationship to it.
--  Therefore the parent call is not required, but useful anyway.

--  I'm not very certain about the following idea, but it might be useful
--  in some circustances:
extending procedure Do_A (Object : Grand_Child) is
   pragma No_Parent_Call;
begin
   Do_All;
   --  We have hacked with internal data structures and done the job of
   --  parents Do_A. Calling the praent's operation could be disastrous.
end Do_A;

If the type Parent in my example was declared to be private (or
tagged private) the parent cal would only be allowed when the type
is fully visible, since calling parent's operations when the overridden
ones should actually be called can result in bad things.

-- 
Tapio



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

* [Offtopic] Re: Constructing an object
  2005-09-23 14:23           ` Matthew Heaney
@ 2006-01-17  6:28             ` James Dennett
  0 siblings, 0 replies; 34+ messages in thread
From: James Dennett @ 2006-01-17  6:28 UTC (permalink / raw)


Matthew Heaney wrote:
> Hyman Rosen wrote:
> 
>>Just to correct this, C++ does not require a default constructor
>>for objects which are components of classes, but does require them
>>for objects which are elements of arrays.
> 
> 
> Right.  I was really thinking of (not being able to use) the class as a
> container element when I wrote that.
> 

C++ standard library containers do not require the
type of their elements to have a default constructor
either; they must be CopyConstructible and
Asssignable though.

-- James



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

end of thread, other threads:[~2006-01-17  6:28 UTC | newest]

Thread overview: 34+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2005-09-21  8:46 Constructing an object Maciej Sobczak
2005-09-21 10:16 ` Georg Bauhaus
2005-09-22  7:21   ` Maciej Sobczak
2005-09-21 11:55 ` Dmitry A. Kazakov
2005-09-22  7:28   ` Maciej Sobczak
2005-09-22  7:45     ` Maciej Sobczak
2005-09-22 13:33       ` Dmitry A. Kazakov
2005-09-24  5:23         ` Randy Brukardt
2005-09-24  9:47           ` Dmitry A. Kazakov
2005-09-29  0:12             ` Randy Brukardt
2005-09-29  8:17               ` Dmitry A. Kazakov
2005-09-29 22:21                 ` Randy Brukardt
2005-09-30  8:14                   ` Jean-Pierre Rosen
2005-09-30 19:28                     ` Dmitry A. Kazakov
2005-09-30 17:49                   ` Robert A Duff
2005-10-01  0:44                     ` Randy Brukardt
2005-10-01 10:49                       ` Dmitry A. Kazakov
2005-10-01 11:06                       ` Tapio Kelloniemi
2005-10-01 14:13                         ` Robert A Duff
2005-10-02 11:52                           ` Tapio Kelloniemi
2005-10-01 15:19                       ` Georg Bauhaus
2005-09-23  5:40 ` Matthew Heaney
2005-09-23  7:18   ` tmoran
2005-09-23  8:23   ` Maciej Sobczak
2005-09-23 12:04     ` Dmitry A. Kazakov
2005-09-23 12:36       ` Matthew Heaney
2005-09-23 13:03         ` Hyman Rosen
2005-09-23 13:41           ` Maciej Sobczak
2005-09-23 14:23           ` Matthew Heaney
2006-01-17  6:28             ` [Offtopic] " James Dennett
2005-09-23 13:42         ` Dmitry A. Kazakov
2005-09-23 14:27           ` Matthew Heaney
2005-09-23 12:24     ` Matthew Heaney
2005-09-24  5:34       ` Randy Brukardt

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