comp.lang.ada
 help / color / mirror / Atom feed
* Discriminant name must be alone
@ 2002-12-21 19:22 Michal Nowak
  2002-12-21 23:08 ` Robert A Duff
  0 siblings, 1 reply; 6+ messages in thread
From: Michal Nowak @ 2002-12-21 19:22 UTC (permalink / raw)


Hello all,

Digging around (teaching myself and experimenting with Ada)
I wandered to RM 3.8 (12):

"A name that denotes a noninherited discriminant is allowed within
the declaration of the type, but not within the discriminant_part.
If the discriminant is used to define the constraint of a component,
the bounds of an entry family, or the constraint of the parent subtype
in a derived_type_definition then its name shall appear alone as a
direct_name (not as part of a larger expression or expanded name).
A discriminant shall not be used to define the constraint of a scalar
component."

At this point (maybe later I will get deeper in other uses) I'm 
thinking about using a discriminant to define the constraint of
a component. This rule forbids me to do something like this:

type R (Start_Ind : Positive) is
   record
      Table : Some_Array_Type (1 .. Start_Ind + 10);
   end record;

and like this:

type R (Start_Ind : Positive) is
   record
      Table : Some_Array_Type (Start_Ind .. Start_Ind + 10);
   end record;

I may live with this, but I don't like to blindly follow
the rules, I'm a bit more curious :-). AARM (12a):
"Reason: The penultimate restriction simplifies implementation,
and allows the outer discriminant and the inner discriminant or
bound to possibly share storage."
gives me some explanation to this. In fact, in my first example,
if the array was constrained by (1 .. Start_Ind), Start_Ind may
be shared by record and array, but if it is (1 .. Start_Ind + 10)
it cannot. In the second example Start_Ind may be shared, even
if it breaks the rule two times, but that's only a specific example
and does not shows my point.
My questions: Why is this rule so restrictive? Is the (potential)
possibility or simpler implementation only reason for it? Maybe
there are historical (I may be too young to know them - I'm just
freshly after studies) or so? If someone has any pointers I'll be
glad to read them. 
From the other side - if the possibility of shared storage is
the only one, maybe it would be reasonable to weaken this
rule in next Ada revision, to allow uses like above? So the
memory usage depends on how discriminants are used. Are there
any uses (examples), where it is reasonable to keep this rule
in current form?

Regards,
   - Michal



-- -----------------------------------------------------------------
--   ___        _
--  / _ \      | |                      I Choose Ada:
-- | |_| |  ___| |   _____   The Most Trusted Name in Software (TM)
-- |  _  | | __  |  | __  | 
-- |_| |_| |_____|_ |_____|_ http://www.adaic.org/whyada/choose.html
--
-- -----------------------------------------------------------------




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

* Re: Discriminant name must be alone
  2002-12-21 19:22 Discriminant name must be alone Michal Nowak
@ 2002-12-21 23:08 ` Robert A Duff
  2002-12-23 11:21   ` Dmitry A. Kazakov
  2002-12-25 14:29   ` Michal Nowak
  0 siblings, 2 replies; 6+ messages in thread
From: Robert A Duff @ 2002-12-21 23:08 UTC (permalink / raw)


Michal Nowak <vinnie@inetia.pl> writes:

> Hello all,
> 
> Digging around (teaching myself and experimenting with Ada)
> I wandered to RM 3.8 (12):

I suggest using a textbook (perhaps Barnes) to learn Ada, rather than
the RM.  Unless you're very comfortable reading all the legalese in the
RM (and AARM)...

> "A name that denotes a noninherited discriminant is allowed within
> the declaration of the type, but not within the discriminant_part.
> If the discriminant is used to define the constraint of a component,
> the bounds of an entry family, or the constraint of the parent subtype
> in a derived_type_definition then its name shall appear alone as a
> direct_name (not as part of a larger expression or expanded name).
> A discriminant shall not be used to define the constraint of a scalar
> component."
> 
> At this point (maybe later I will get deeper in other uses) I'm 
> thinking about using a discriminant to define the constraint of
> a component. This rule forbids me to do something like this:
> 
> type R (Start_Ind : Positive) is
>    record
>       Table : Some_Array_Type (1 .. Start_Ind + 10);
>    end record;
> 
> and like this:
> 
> type R (Start_Ind : Positive) is
>    record
>       Table : Some_Array_Type (Start_Ind .. Start_Ind + 10);
>    end record;

That's right.  Those are illegal.

> I may live with this, but I don't like to blindly follow
> the rules, I'm a bit more curious :-). AARM (12a):
> "Reason: The penultimate restriction simplifies implementation,
> and allows the outer discriminant and the inner discriminant or
> bound to possibly share storage."
> gives me some explanation to this. In fact, in my first example,
> if the array was constrained by (1 .. Start_Ind), Start_Ind may
> be shared by record and array, but if it is (1 .. Start_Ind + 10)
> it cannot. In the second example Start_Ind may be shared, even
> if it breaks the rule two times, but that's only a specific example
> and does not shows my point.
> My questions: Why is this rule so restrictive? Is the (potential)
> possibility or simpler implementation only reason for it? 

The implementation issue is not just "potential".
It really does simplify implementation to know that the bound
can be computed by a simple fetch of the discriminant.

>...Maybe
> there are historical (I may be too young to know them - I'm just
> freshly after studies) or so? If someone has any pointers I'll be
> glad to read them. 
> From the other side - if the possibility of shared storage is
> the only one, maybe it would be reasonable to weaken this
> rule in next Ada revision, to allow uses like above? So the
> memory usage depends on how discriminants are used. Are there
> any uses (examples), where it is reasonable to keep this rule
> in current form?

Most uses have no problem with the current rule.  The usual thing is
that the discriminant is the upper bound of an array.  Not 10 less than
the upper bound, or some other convolution.

However, I have run into a few cases where I would like to do what
you're suggesting here.  For example, I wrote a "storage pool" type,
where the private part defined the storage as an array of machine words.
The allocated types were known to be always word aligned, and always a
multiple of word size.  The array of words makes alignment easy, and the
Allocate procedure can simply return 'Address of one of the array
components.  But I wanted the client to specify the size in
Storage_Elements.  So the array would have to be:

    Storage: Word_Array(0..(Size_In_Bytes-1)/4); -- Illegal!

where Size_In_Bytes is the discriminant.  Or something like that.

I think the main problem with allowing that (besides what the AARM
mentions) is that if you allow arbitrary expressions referring to
discriminants, you have to worry about when that expression is
evaluated.  Suppose that expression is "F(Size_In_Bytes)", where F is
some function that might have side effects.  If it has side effects, it
might return a different answer each time it's called.

I suppose one answer is to say that the expression is evaluated whenever
a value of the record type is created.  The value of that expression
must be saved as "dope" -- a hidden record component created by the
compiler.  If it has side effects, that's the programmer's problem.
This would be the first case where a compiler is required to store
dope.  Some compilers store dope, but it's never required.

Another answer is, "forbid side effects in those expressions", so the
compiler can evaluate those expressions whenever it likes.  Ada has no
such mechanism, currently.  To provide such a mechanism, each function
would have to declare (on the spec) what variables it meddles with --
as in SPARK.  If the expression "Start_Ind + 10" is allowed, then surely
you should be allowed to call a function that does "return Start_Ind + 10;".

So I think your suggestion is reasonable, but would require a
substantial change to the language and to compilers, and is only useful
in rare cases, so is unlikely to happen.

- Bob



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

* Re: Discriminant name must be alone
  2002-12-21 23:08 ` Robert A Duff
@ 2002-12-23 11:21   ` Dmitry A. Kazakov
  2002-12-23 14:00     ` Robert A Duff
  2002-12-25 14:29   ` Michal Nowak
  1 sibling, 1 reply; 6+ messages in thread
From: Dmitry A. Kazakov @ 2002-12-23 11:21 UTC (permalink / raw)


Robert A Duff wrote:

> However, I have run into a few cases where I would like to do what
> you're suggesting here.  For example, I wrote a "storage pool" type,
> where the private part defined the storage as an array of machine words.
> The allocated types were known to be always word aligned, and always a
> multiple of word size.  The array of words makes alignment easy, and the
> Allocate procedure can simply return 'Address of one of the array
> components.  But I wanted the client to specify the size in
> Storage_Elements.  So the array would have to be:
> 
>     Storage: Word_Array(0..(Size_In_Bytes-1)/4); -- Illegal!
> 
> where Size_In_Bytes is the discriminant.  Or something like that.
>
> I think the main problem with allowing that (besides what the AARM
> mentions) is that if you allow arbitrary expressions referring to
> discriminants, you have to worry about when that expression is
> evaluated.  Suppose that expression is "F(Size_In_Bytes)", where F is
> some function that might have side effects.  If it has side effects, it
> might return a different answer each time it's called.
> 
> I suppose one answer is to say that the expression is evaluated whenever
> a value of the record type is created.  The value of that expression
> must be saved as "dope" -- a hidden record component created by the
> compiler.  If it has side effects, that's the programmer's problem.
> This would be the first case where a compiler is required to store
> dope.  Some compilers store dope, but it's never required.
> 
> Another answer is, "forbid side effects in those expressions", so the
> compiler can evaluate those expressions whenever it likes.  Ada has no
> such mechanism, currently.  To provide such a mechanism, each function
> would have to declare (on the spec) what variables it meddles with --
> as in SPARK.  If the expression "Start_Ind + 10" is allowed, then surely
> you should be allowed to call a function that does "return Start_Ind +
> 10;".

There is a third answer: "user-defined constructors". Should you have a 
user-defined constructor for a discriminated type, you could evaluate its 
discriminants during object construction, which would have same effect as 
if they were expressions (with the arguments provided by the constructor's 
argument list).

User-defined constructors could also solve the "assignment problem", because 
an assignment can be easily generated out of copy-constructor.

-- 
Regards,
Dmitry A. Kazakov
www.dmitry-kazakov.de



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

* Re: Discriminant name must be alone
  2002-12-23 11:21   ` Dmitry A. Kazakov
@ 2002-12-23 14:00     ` Robert A Duff
  2002-12-24 11:16       ` Dmitry A. Kazakov
  0 siblings, 1 reply; 6+ messages in thread
From: Robert A Duff @ 2002-12-23 14:00 UTC (permalink / raw)


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

> There is a third answer: "user-defined constructors".

What do you mean by "user-defined constructors"?  How do they differ
from functions?  And how do they allow nested constraints to depend
upon arbitrary expressions involving discriminants?

- Bob



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

* Re: Discriminant name must be alone
  2002-12-23 14:00     ` Robert A Duff
@ 2002-12-24 11:16       ` Dmitry A. Kazakov
  0 siblings, 0 replies; 6+ messages in thread
From: Dmitry A. Kazakov @ 2002-12-24 11:16 UTC (permalink / raw)


Robert A Duff wrote:

> "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> writes:
> 
>> There is a third answer: "user-defined constructors".
> 
> What do you mean by "user-defined constructors"?  How do they differ
> from functions?

A function returns some object. To use it you must have an assignment. This 
would not work for limited types. A constructor returns nothing. For

type Buffer (Length : Positive) is ...;

Buffer (*) could be viewed as a "call to constructor". Constructor works 
even if Buffer is limited:

X : Buffer (12);

> And how do they allow nested constraints to depend
> upon arbitrary expressions involving discriminants?

Presently we have only predefined "constructors" with the parameters = 
discriminats. Let we have an ability to define a constructor with other 
parameters. Then it would evaluate the discriminants from that parameters. 
Further, a discriminated type could be declared with <> as the discriminant 
to hide the true disriminants in the private part making them an 
implementation detail.

I have a vague idea how to provide constructors for Ada, but I rather start 
a separate thread for it.

-- 
Regards,
Dmitry A. Kazakov
www.dmitry-kazakov.de



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

* Re: Discriminant name must be alone
  2002-12-21 23:08 ` Robert A Duff
  2002-12-23 11:21   ` Dmitry A. Kazakov
@ 2002-12-25 14:29   ` Michal Nowak
  1 sibling, 0 replies; 6+ messages in thread
From: Michal Nowak @ 2002-12-25 14:29 UTC (permalink / raw)


On 2002-12-21 at 23:08 Robert A Duff wrote:

>Michal Nowak <vinnie@inetia.pl> writes:
>
>> Hello all,
>> 
>> Digging around (teaching myself and experimenting with Ada)
>> I wandered to RM 3.8 (12):
>
>I suggest using a textbook (perhaps Barnes) to learn Ada, rather than
>the RM.  Unless you're very comfortable reading all the legalese in the
>RM (and AARM)...

That's OK, I got Cohen's superior "Ada as a Second Language". The rule
I was referring to is described in point 9.2.4 (Allowable use of 
discriminants), but there was no explanation of internal matters 
and why it is like that.

>
>The implementation issue is not just "potential".
>It really does simplify implementation to know that the bound
>can be computed by a simple fetch of the discriminant.
>

[snip some of my questions]

>
>Most uses have no problem with the current rule.  The usual thing is
>that the discriminant is the upper bound of an array.  Not 10 less than
>the upper bound, or some other convolution.
>
>However, I have run into a few cases where I would like to do what
>you're suggesting here.  For example, I wrote a "storage pool" type,
>where the private part defined the storage as an array of machine words.
>The allocated types were known to be always word aligned, and always a
>multiple of word size.  The array of words makes alignment easy, and the
>Allocate procedure can simply return 'Address of one of the array
>components.  But I wanted the client to specify the size in
>Storage_Elements.  So the array would have to be:
>
>    Storage: Word_Array(0..(Size_In_Bytes-1)/4); -- Illegal!
>
>where Size_In_Bytes is the discriminant.  Or something like that.


I forget to mention, that I had immutable records in mind, this
issue is more complicated with mutable records.

I think one time I wanted an array, which was a component of record,
to start at specified index, but the discriminant was Size. It was
impossible to compute ending bound.

>I think the main problem with allowing that (besides what the AARM
>mentions) is that if you allow arbitrary expressions referring to
>discriminants, you have to worry about when that expression is
>evaluated.  Suppose that expression is "F(Size_In_Bytes)", where F is
>some function that might have side effects.  If it has side effects, it
>might return a different answer each time it's called.
>
>I suppose one answer is to say that the expression is evaluated whenever
>a value of the record type is created.  The value of that expression
>must be saved as "dope" -- a hidden record component created by the
>compiler.  If it has side effects, that's the programmer's problem.
>This would be the first case where a compiler is required to store
>dope.  Some compilers store dope, but it's never required.

Now I get the point. 

Thank you for answer and explanations,
Merry Christmas,
   - Michal



-- -----------------------------------------------------------------
-- * ___  *     _'  '   *    `        *   `     *      '      .
--  / _ \  ' * | |    *   *      *                           ~*~
-- | |_| |  ___| | * _____    `      *  `  *       *    `   i/:\i
-- |  _ *| | __  |  | __' |   *                            i/`:'\i
-- |_| |_| |____*|_ |_____|_  Christmas with Ada:  `      i/`':`'\i
--   *      '       http://www.autopen.com/OTANBAUM.shtml '^^^I^^^'
-- -----------------------------------------------------------------








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

end of thread, other threads:[~2002-12-25 14:29 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2002-12-21 19:22 Discriminant name must be alone Michal Nowak
2002-12-21 23:08 ` Robert A Duff
2002-12-23 11:21   ` Dmitry A. Kazakov
2002-12-23 14:00     ` Robert A Duff
2002-12-24 11:16       ` Dmitry A. Kazakov
2002-12-25 14:29   ` Michal Nowak

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