comp.lang.ada
 help / color / mirror / Atom feed
* Construction initialization problem
@ 2008-12-04 16:08 Dmitry A. Kazakov
  2008-12-04 17:35 ` Adam Beneschan
  2008-12-04 22:17 ` Randy Brukardt
  0 siblings, 2 replies; 8+ messages in thread
From: Dmitry A. Kazakov @ 2008-12-04 16:08 UTC (permalink / raw)


Consider this:

   type A is tagged limited null record;
   type B (X : not null access A'Class) is tagged limited null record;

Now we want to specialize B so that its instances would already contain A:

   type C is new B with record
      Y : aliased A;
   end record;

Now there seems no way to either create an object of C so that C.X would
point to C.Y or else to derive from C a new type without discriminant:

   type D is new C (X => C.Y'Access) with null record; -- Illegal
   type D is new C (X => D.Y'Access) with null record; -- Illegal

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



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

* Re: Construction initialization problem
  2008-12-04 16:08 Construction initialization problem Dmitry A. Kazakov
@ 2008-12-04 17:35 ` Adam Beneschan
  2008-12-04 22:17 ` Randy Brukardt
  1 sibling, 0 replies; 8+ messages in thread
From: Adam Beneschan @ 2008-12-04 17:35 UTC (permalink / raw)


On Dec 4, 8:08 am, "Dmitry A. Kazakov" <mail...@dmitry-kazakov.de>
wrote:
> Consider this:
>
>    type A is tagged limited null record;
>    type B (X : not null access A'Class) is tagged limited null record;
>
> Now we want to specialize B so that its instances would already contain A:
>
>    type C is new B with record
>       Y : aliased A;
>    end record;
>
> Now there seems no way to either create an object of C so that C.X would
> point to C.Y or else to derive from C a new type without discriminant:
>
>    type D is new C (X => C.Y'Access) with null record; -- Illegal
>    type D is new C (X => D.Y'Access) with null record; -- Illegal
>

On a related note, you can't do this either:

   type Rec1 is tagged null record;

   type Rec2 (D : access Rec1) is tagged limited record ... end
record;

   type Rec3 is record
      F1 : aliased Rec1;
      F2 : Rec2 (Rec3.F1'access);
   end record;

The rule preventing this is in 3.8, which says that the only component
of "current instance" that you can use is a noninherited
discriminant---and F1 is not a discriminant.  But offhand, I don't
think this restriction is necessary when the component is used as the
prefix of 'Access.  There shouldn't be any accessibility level issues,
I think.  Some care might be needed to get the language rules right in
case the component is in a variant part, but other than that I can't
think of any difficulties either in adding a rule to allow this or in
implementation.  Of course that doesn't mean there aren't any
difficulties, just that I can't think of them.

Simply adding that rule wouldn't quite solve Dmitry's problem, since
in this case:

   type D is new C (X => D.Y'Access) with null record; -- Illegal

he's trying to use a current instance in a discriminant, which a
different clause in 3.8(12) makes illegal.  Also, the issue of writing
an allocator for type C whose discriminant refers to C would seem to
require new syntax---maybe if we borrow the "extended return" idea?

   New_Obj := new ZZZ : C (X => ZZZ.Y'Access);

                              -- Adam







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

* Re: Construction initialization problem
  2008-12-04 16:08 Construction initialization problem Dmitry A. Kazakov
  2008-12-04 17:35 ` Adam Beneschan
@ 2008-12-04 22:17 ` Randy Brukardt
  2008-12-04 23:02   ` Adam Beneschan
  2008-12-05  9:00   ` Dmitry A. Kazakov
  1 sibling, 2 replies; 8+ messages in thread
From: Randy Brukardt @ 2008-12-04 22:17 UTC (permalink / raw)


"Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message 
news:oscmgxrpod50$.g7h7snlssha0$.dlg@40tude.net...
> Consider this:
>
>   type A is tagged limited null record;
>   type B (X : not null access A'Class) is tagged limited null record;
>
> Now we want to specialize B so that its instances would already contain A:

You never explained why you would want the discriminant in the first place. 
I would think that it would be better to do what you've said about C 
(include the instance directly in it) from the beginning, and the problem 
doesn't come up.

Access discriminants have a fairly high overhead in Ada (because of the 
possibility of coextensions), so unless you *need* coextensions, they ought 
to be avoided.

One could use a named access type instead to declare the discriminant if you 
really need a discriminant. But the only time you must use a discriminant is 
if you have a discriminant-dependent component, and there is no interesting 
way to make a component dependent on an access discriminant. So I'd prefer 
to just avoid the discriminant.

If you're using discriminants to stand in for a proper constructor, I say 
"don't!!" Use a constructor function as the language intends (and force it 
with a (<>) if you need it).

                               Randy.






>   type C is new B with record
>      Y : aliased A;
>   end record;
>
> Now there seems no way to either create an object of C so that C.X would
> point to C.Y or else to derive from C a new type without discriminant:
>
>   type D is new C (X => C.Y'Access) with null record; -- Illegal
>   type D is new C (X => D.Y'Access) with null record; -- Illegal
>
> -- 
> Regards,
> Dmitry A. Kazakov
> http://www.dmitry-kazakov.de 





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

* Re: Construction initialization problem
  2008-12-04 22:17 ` Randy Brukardt
@ 2008-12-04 23:02   ` Adam Beneschan
  2008-12-06  1:47     ` Randy Brukardt
  2008-12-05  9:00   ` Dmitry A. Kazakov
  1 sibling, 1 reply; 8+ messages in thread
From: Adam Beneschan @ 2008-12-04 23:02 UTC (permalink / raw)


On Dec 4, 2:17 pm, "Randy Brukardt" <ra...@rrsoftware.com> wrote:
> "Dmitry A. Kazakov" <mail...@dmitry-kazakov.de> wrote in messagenews:oscmgxrpod50$.g7h7snlssha0$.dlg@40tude.net...
>
> > Consider this:
>
> >   type A is tagged limited null record;
> >   type B (X : not null access A'Class) is tagged limited null record;
>
> > Now we want to specialize B so that its instances would already contain A:
>
> You never explained why you would want the discriminant in the first place.
> I would think that it would be better to do what you've said about C
> (include the instance directly in it) from the beginning, and the problem
> doesn't come up.
>
> Access discriminants have a fairly high overhead in Ada (because of the
> possibility of coextensions), so unless you *need* coextensions, they ought
> to be avoided.
>
> One could use a named access type instead to declare the discriminant if you
> really need a discriminant. But the only time you must use a discriminant is
> if you have a discriminant-dependent component, and there is no interesting
> way to make a component dependent on an access discriminant. So I'd prefer
> to just avoid the discriminant.

I thought access discriminants---at least those with anonymous access
type---were put into Ada 95 (before there were coextensions) to allow
objects to point to other objects in a nested scope, in a restricted
way (i.e. only in limited types) that prevented those references to a
nested objects from staying around after the nested object
disappeared.  I wasn't aware that there were enough new features in
Ada 2005 to provide this ability without using access discriminants.
So what am I missing?

                                 -- Adam



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

* Re: Construction initialization problem
  2008-12-04 22:17 ` Randy Brukardt
  2008-12-04 23:02   ` Adam Beneschan
@ 2008-12-05  9:00   ` Dmitry A. Kazakov
  2008-12-06  1:42     ` Randy Brukardt
  1 sibling, 1 reply; 8+ messages in thread
From: Dmitry A. Kazakov @ 2008-12-05  9:00 UTC (permalink / raw)


On Thu, 4 Dec 2008 16:17:56 -0600, Randy Brukardt wrote:

> "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message 
> news:oscmgxrpod50$.g7h7snlssha0$.dlg@40tude.net...
>> Consider this:
>>
>>   type A is tagged limited null record;
>>   type B (X : not null access A'Class) is tagged limited null record;
>>
>> Now we want to specialize B so that its instances would already contain A:
> 
> You never explained why you would want the discriminant in the first place.

Huh, that is a mix-in, a major excuse for not to have multiple inheritance
in Ada!
 
> I would think that it would be better to do what you've said about C 
> (include the instance directly in it) from the beginning, and the problem 
> doesn't come up.

No, in the case I have in mind, mix-in is appropriate. Consider layered
network protocols. "A" were a transport layer. "B" were a protocol. There
can be many different transports for the same protocol so an implementation
of B shall dispatch to the operations of A. If B would inherit to A that
would require permanent conversions to A'Class inside the operations of B
(=a mess). Now consider that at some point you decided which transport
(some type derived from A) and which protocol (some type derived from B) to
take. You want to put it all into one opaque object. That would be the type
C. But you cannot, without pointers and dynamic allocation. Welcome back to
C++!

> Access discriminants have a fairly high overhead in Ada (because of the 
> possibility of coextensions), so unless you *need* coextensions, they ought 
> to be avoided.

Do you mean space or time overhead?

> One could use a named access type instead to declare the discriminant if you 
> really need a discriminant.

You mean an access component? But because Ada has no proper construction,
there is no way to use not-null access components. OK, I know and in fact
use one, but it is very intrusive.

> But the only time you must use a discriminant is 
> if you have a discriminant-dependent component, and there is no interesting 
> way to make a component dependent on an access discriminant. So I'd prefer 
> to just avoid the discriminant.
> 
> If you're using discriminants to stand in for a proper constructor, I say 
> "don't!!" Use a constructor function as the language intends (and force it 
> with a (<>) if you need it).

Oh no, been there. The problem arose from inability to write such a
function! You must be able to write an aggregate to return it from the
function. The components of the aggregate will have the constraints
impossible to spell.

----------------------------
The language problem is lack of abstraction. If there were abstract access
types, then you could make an instance of A implement the interface of
"access A" and them simply put an object of A as a discriminant for B:

   type AA is new A and access A with null record; -- A and access to A
   overriding function "'Access" (X : A) return access A;
   function Create return AA; -- Creates an instance of AA
   B_with_A : B (Create); -- Constrain it by an object

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



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

* Re: Construction initialization problem
  2008-12-05  9:00   ` Dmitry A. Kazakov
@ 2008-12-06  1:42     ` Randy Brukardt
  2008-12-06 10:16       ` Dmitry A. Kazakov
  0 siblings, 1 reply; 8+ messages in thread
From: Randy Brukardt @ 2008-12-06  1:42 UTC (permalink / raw)


"Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message 
news:d16z537mbee4$.wp9rmx0b7kjf.dlg@40tude.net...
> On Thu, 4 Dec 2008 16:17:56 -0600, Randy Brukardt wrote:
>
>> "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message
>> news:oscmgxrpod50$.g7h7snlssha0$.dlg@40tude.net...
>>> Consider this:
>>>
>>>   type A is tagged limited null record;
>>>   type B (X : not null access A'Class) is tagged limited null record;
>>>
>>> Now we want to specialize B so that its instances would already contain 
>>> A:
>>
>> You never explained why you would want the discriminant in the first 
>> place.
>
> Huh, that is a mix-in, a major excuse for not to have multiple inheritance
> in Ada!

A waste of effort. I don't believe in multiple inheritance (in any form); it 
seems to me to be an excuse for sloppy design.

...
> No, in the case I have in mind, mix-in is appropriate. Consider layered
> network protocols. "A" were a transport layer. "B" were a protocol. There
> can be many different transports for the same protocol so an 
> implementation
> of B shall dispatch to the operations of A. If B would inherit to A that
> would require permanent conversions to A'Class inside the operations of B
> (=a mess).

I agree. I think it would be best to keep the two types totally separate. 
And use a function constructor to glue them together.

> Now consider that at some point you decided which transport
> (some type derived from A) and which protocol (some type derived from B) 
> to
> take. You want to put it all into one opaque object. That would be the 
> type
> C.

I'm dubious that this is a good idea. But I think you could do so just fine 
with an appropriate constructor function.

> But you cannot, without pointers and dynamic allocation. Welcome back to 
> C++!

If you have to resort to a dynamically allocated component, just wrap it in 
a holder container. Then there is no possibility of data loss or (visible) 
dynamic allocation. It would be nice to have a way to access the object 
directly in such a container (a problem that I am working on for Amendment 
2), but that is not a critical part to be able to do this.

>> Access discriminants have a fairly high overhead in Ada (because of the
>> possibility of coextensions), so unless you *need* coextensions, they 
>> ought
>> to be avoided.
>
> Do you mean space or time overhead?

Yes. :-)

...
>> But the only time you must use a discriminant is
>> if you have a discriminant-dependent component, and there is no 
>> interesting
>> way to make a component dependent on an access discriminant. So I'd 
>> prefer
>> to just avoid the discriminant.
>>
>> If you're using discriminants to stand in for a proper constructor, I say
>> "don't!!" Use a constructor function as the language intends (and force 
>> it
>> with a (<>) if you need it).
>
> Oh no, been there. The problem arose from inability to write such a
> function! You must be able to write an aggregate to return it from the
> function. The components of the aggregate will have the constraints
> impossible to spell.

The reason that we added extended return statements to the language is so 
that you don't have to write the entire return thing as an aggregate. The 
only reason you would have to do that is because you used a discriminant 
rather than a component. If it hurts, *don't do that*!!

> ----------------------------
> The language problem is lack of abstraction. If there were abstract access
> types, then you could make an instance of A implement the interface of
> "access A" and them simply put an object of A as a discriminant for B:
>
>   type AA is new A and access A with null record; -- A and access to A
>   overriding function "'Access" (X : A) return access A;
>   function Create return AA; -- Creates an instance of AA
>   B_with_A : B (Create); -- Constrain it by an object

I don't understand this point at all. But it doesn't matter, because a 
function like your function "access" doesn't work: the accessibility will be 
wrong. Currently it would be library level, meaning that you could only 
return 'Unchecked_Access and essentially everything would be a dangling 
pointer. Tucker has a proposal to fix that (AI05-0051-1), but it requires a 
lot of additional runtime overhead for functions returning anonymous access 
types. That's probably not what you want, especially given that you could 
not do much with the result other than dereferencing it. (With Tucker's 
current proposal, 'Access would still always be illegal; that can be fixed 
to allow accessibility to the point of the call, but that doesn't help if 
you want to keep the access longer.)

I've been trying to work on this problem, but the obvious solutions would 
require full dynamic accessibility checks, including passing the 
accessibility of all by-reference parameters -- and that is way too 
expensive to consider. Plus dynamic checks provide a new failure mechanism 
for code; it's not clear that is an advantage.

I think that I might be able to fix the problem in the context of the 
containers only, and for dereference of the objects only, but it is not 
clear that the fix is worth the effort.

                                                       Randy.





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

* Re: Construction initialization problem
  2008-12-04 23:02   ` Adam Beneschan
@ 2008-12-06  1:47     ` Randy Brukardt
  0 siblings, 0 replies; 8+ messages in thread
From: Randy Brukardt @ 2008-12-06  1:47 UTC (permalink / raw)


"Adam Beneschan" <adam@irvine.com> wrote in message 
news:b5578996-6595-499c-8031-3bfae82a6070@s9g2000prm.googlegroups.com...
...
> I thought access discriminants---at least those with anonymous access
> type---were put into Ada 95 (before there were coextensions) to allow
> objects to point to other objects in a nested scope, in a restricted
> way (i.e. only in limited types) that prevented those references to a
> nested objects from staying around after the nested object
> disappeared.

There always were coextensions in Ada 95; they just didn't have that name.

You might be right about the original reason, but like everything else about 
anonymous access types, it doesn't work in practice. Best thing to do with 
them is to forget that they exist.

> I wasn't aware that there were enough new features in
> Ada 2005 to provide this ability without using access discriminants.
> So what am I missing?

I don't think this capability is (usefully) provided in Ada 95, either, 
because you don't want these things to be discriminants - that triggers all 
kinds of other rules (such as the requirement to use full record aggregates) 
that get in the way more than help. Coextensions probably are the only way 
to do this usefully, but they're very complex to implement and understand, 
and people don't use them anyway because they *look* like dynamic allocation 
(even though they are not supposed to be implemented that way).

This is the sort of thing that looks good in toy examples, but falls apart 
when you actually try to use it (as Dmitry noted in his original question).

                                                                  Randy.





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

* Re: Construction initialization problem
  2008-12-06  1:42     ` Randy Brukardt
@ 2008-12-06 10:16       ` Dmitry A. Kazakov
  0 siblings, 0 replies; 8+ messages in thread
From: Dmitry A. Kazakov @ 2008-12-06 10:16 UTC (permalink / raw)


On Fri, 5 Dec 2008 19:42:40 -0600, Randy Brukardt wrote:

>> But you cannot, without pointers and dynamic allocation. Welcome back to 
>> C++!
> 
> If you have to resort to a dynamically allocated component, just wrap it in 
> a holder container.

That does not help. The inner container will have to propagate its
constraint to the outer container. The only solution to this is to replace
the outer container's constraint (anonymous not null access discriminant)
with a pointer. Subsequently, that pointer cannot be a not-null pointer.

> Then there is no possibility of data loss or (visible) 
> dynamic allocation. It would be nice to have a way to access the object 
> directly in such a container (a problem that I am working on for Amendment 
> 2), but that is not a critical part to be able to do this.

Critical parts are:

1. an explicit use of heap
2. not-null constraint is lost

It is just C++ written in Ada.

>>> Access discriminants have a fairly high overhead in Ada (because of the
>>> possibility of coextensions), so unless you *need* coextensions, they 
>>> ought to be avoided.
>>
>> Do you mean space or time overhead?
> 
> Yes. :-)

Can you elaborate on this. Considering:

   type T1 is ...
   procedure F1 (Object : in out T1);

   type T2 (X : not null access T1'Class) is ...
   procedure F2 (Object : T2) is
   begin
       Object.X.F1; -- Accessibility checks here?

I hoped that the compiler should drop any checks because X is a not-null
discriminant.

>>> But the only time you must use a discriminant is
>>> if you have a discriminant-dependent component, and there is no 
>>> interesting
>>> way to make a component dependent on an access discriminant. So I'd 
>>> prefer
>>> to just avoid the discriminant.
>>>
>>> If you're using discriminants to stand in for a proper constructor, I say
>>> "don't!!" Use a constructor function as the language intends (and force 
>>> it
>>> with a (<>) if you need it).
>>
>> Oh no, been there. The problem arose from inability to write such a
>> function! You must be able to write an aggregate to return it from the
>> function. The components of the aggregate will have the constraints
>> impossible to spell.
> 
> The reason that we added extended return statements to the language is so 
> that you don't have to write the entire return thing as an aggregate. The 
> only reason you would have to do that is because you used a discriminant 
> rather than a component. If it hurts, *don't do that*!!

I don't see how a function may help here. Let us rewrite the stuff to
pointers as you suggest:

   type A is tagged limited null record;
   type A_Ptr is not null access all A'Class;
   type B is tagged limited record
      X : A_Ptr;
   end record;
   type C is new B with record
      Y : aliased A;
   end record;
   function Create return C is
   begin
      return Result : C do
         Result.X := Result.Y'Unchecked_Access;
      end return;
   end Create;

This will propagate Constraint_Error upon an attempt to create a C with
Create.

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



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

end of thread, other threads:[~2008-12-06 10:16 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2008-12-04 16:08 Construction initialization problem Dmitry A. Kazakov
2008-12-04 17:35 ` Adam Beneschan
2008-12-04 22:17 ` Randy Brukardt
2008-12-04 23:02   ` Adam Beneschan
2008-12-06  1:47     ` Randy Brukardt
2008-12-05  9:00   ` Dmitry A. Kazakov
2008-12-06  1:42     ` Randy Brukardt
2008-12-06 10:16       ` Dmitry A. Kazakov

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