comp.lang.ada
 help / color / mirror / Atom feed
* advice on package design
@ 2005-03-07 16:23 spambox
  2005-03-07 21:08 ` Dmitry A. Kazakov
  0 siblings, 1 reply; 28+ messages in thread
From: spambox @ 2005-03-07 16:23 UTC (permalink / raw)


Hello,
I've written a package for linked list operations for my personal use.
After a while I figured I'd write another package for some string
operations that I use frequently. On their own, they both work well.
Now comes the problem.
My string library uses my linked list library, which must be
instantiated -- the structure of a node must be supplied. After that,
the string library can use procedures from the linked list library. But
what about the end user? The linked list library defines a type, say
linked_list, and the string library instantiates it something like:

type word is record ...
package string_ll is new linkedlists(word);
use string_ll;

Ultimatelly, if some function from my strings package returns a
linked_list (as defined in the linked list package), it will be of the
type string_ll.linked_list, if we follow the above example. How could
it be available for further processing with routines from the linked
list package?

I could "use string_ll" (exactly as in the package) in my end program,
however that's probably a poor design, since the user need not know how
something was instantiated in another package.
If, on the other hand, I make another instance of linkedlist, I must
supply the same argument the string package does ("word" in the above
example), but then the resulting linked_list type would be
some_new_name.linked_list, which is not compatible with what the string
package uses (string_ll.linked_list in the example).

What is the correct way of dealing with such problems?

Thanks,

 andrej




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

* Re: advice on package design
  2005-03-07 16:23 advice on package design spambox
@ 2005-03-07 21:08 ` Dmitry A. Kazakov
  2005-03-08 12:48   ` spambox
  2005-03-12 19:57   ` Robert A Duff
  0 siblings, 2 replies; 28+ messages in thread
From: Dmitry A. Kazakov @ 2005-03-07 21:08 UTC (permalink / raw)


On 7 Mar 2005 08:23:58 -0800, spambox@volja.net wrote:

> Hello,
> I've written a package for linked list operations for my personal use.
> After a while I figured I'd write another package for some string
> operations that I use frequently. On their own, they both work well.
> Now comes the problem.
> My string library uses my linked list library, which must be
> instantiated -- the structure of a node must be supplied. After that,
> the string library can use procedures from the linked list library. But
> what about the end user? The linked list library defines a type, say
> linked_list, and the string library instantiates it something like:
> 
> type word is record ...
> package string_ll is new linkedlists(word);
> use string_ll;
> 
> Ultimatelly, if some function from my strings package returns a
> linked_list (as defined in the linked list package), it will be of the
> type string_ll.linked_list, if we follow the above example. How could
> it be available for further processing with routines from the linked
> list package?
> 
> I could "use string_ll" (exactly as in the package) in my end program,
> however that's probably a poor design, since the user need not know how
> something was instantiated in another package.

The public part of the package is its interface. If you instantiate
something there, then it is OK for all to use it.

> If, on the other hand, I make another instance of linkedlist, I must
> supply the same argument the string package does ("word" in the above
> example), but then the resulting linked_list type would be
> some_new_name.linked_list, which is not compatible with what the string
> package uses (string_ll.linked_list in the example).

Yes. Ada has named type equivalence.

> What is the correct way of dealing with such problems?

There are many different ways to deal with that. If you have

1. 

package Foo is
   type Word is ...
   package Baz is new Bar (Word,...);
   ...
end Foo;

then of course you can "use" Baz referencing it as Foo.Baz everywhere Foo
is "with"-ed. You can even rename it:

with Foo;
package Baz renames Foo.Baz;

2. You can instantiate your Baz outside Foo but with the things defined in
the public part of Foo:

with Foo;  use Foo;
package Baz is new Bar (Word,...);

Note that the body of Foo may still use Baz:

with Baz;
package body Foo is ... end Foo;

Though beware that there must exits an elaboration order of the
specifications and bodies Foo and Bar.

2.a. If Bar needs privates of Foo, the you can make Bar a generic child of
Foo.

3. You can rename parts of contents of Baz in Foo:

package Foo is
   type Word is ...
   package Baz is new Bar (Word, ...);

   subtype Linked_List is Baz.Linked_List; -- "renaming" of a type
   procedure Insert (...) renames Baz.Insert; -- renaming of a procedure
   ...
end Foo;

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



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

* Re: advice on package design
  2005-03-07 21:08 ` Dmitry A. Kazakov
@ 2005-03-08 12:48   ` spambox
  2005-03-08 17:18     ` Dmitry A. Kazakov
  2005-03-12 19:57   ` Robert A Duff
  1 sibling, 1 reply; 28+ messages in thread
From: spambox @ 2005-03-08 12:48 UTC (permalink / raw)


Dmitry A. Kazakov wrote:

> There are many different ways to deal with that. If you have

Dmitry, thank you very much for you help; this is the second time that
you've posted elaborate answers to my problems. I apreciate it.

> 2. You can instantiate your Baz outside Foo but with the things
defined in
> the public part of Foo:

> Note that the body of Foo may still use Baz:

My thoughts on this solution: the package would have to know how the
user chose to instantiate another package, so that it could use the
same name. On the other hand, let's say the package came with
instructions as to how the package it depends on should be
instantiated. However, that would leave the user no choice for the name
and effectively we would get something similar to your first solution.
I find this approach better; the package `a' comes with instructions,
directing the user to "use" something that was instantiated in `a' with
the same name, of course. Sure, it could also be renamed later.

> 2.a. If Bar needs privates of Foo, the you can make Bar a generic
child of
> Foo.

I have yet to investigate this one.

> 3. You can rename parts of contents of Baz in Foo:

At present, this is the accepted solution. It seems to me that it's
also the most logical for ada? Since we're forced to follow the rules
of strong typing, then it's only logical to have different subroutines
for each type. There are only less than ten subroutines in my linked
lists package, so renaming then was easy. Besides, the program becomes
clearer (in the end, that's what ada encourages us to do, right?) --
now, for example, we can use a word_walk subroutine for word lists, and
eg. node_walk for all the others. Agreed, a good idea.

andrej
--
http://sonet.homelinux.net




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

* Re: advice on package design
  2005-03-08 12:48   ` spambox
@ 2005-03-08 17:18     ` Dmitry A. Kazakov
  0 siblings, 0 replies; 28+ messages in thread
From: Dmitry A. Kazakov @ 2005-03-08 17:18 UTC (permalink / raw)


On 8 Mar 2005 04:48:41 -0800, spambox@volja.net wrote:

> Dmitry A. Kazakov wrote:
> 
>> There are many different ways to deal with that. If you have
> 
> Dmitry, thank you very much for you help; this is the second time that
> you've posted elaborate answers to my problems. I apreciate it.

You are welcome.

>> 2. You can instantiate your Baz outside Foo but with the things
>> defined in the public part of Foo:
> 
>> Note that the body of Foo may still use Baz:
> 
> My thoughts on this solution: the package would have to know how the
> user chose to instantiate another package, so that it could use the
> same name. On the other hand, let's say the package came with
> instructions as to how the package it depends on should be
> instantiated.

Ada also has formal generic packages. If Foo has to be dependent on an
instantiated by user Bar, then you can make it generic:

generic
   type Word is ...;
   with package Baz is new Bar (Word, ...);
package Foo is ... end Foo;

Though this would inverse the dependency between Foo and Baz.

> However, that would leave the user no choice for the name
> and effectively we would get something similar to your first solution.
> I find this approach better; the package `a' comes with instructions,
> directing the user to "use" something that was instantiated in `a' with
> the same name, of course. Sure, it could also be renamed later.
> 
>> 3. You can rename parts of contents of Baz in Foo:
> 
> At present, this is the accepted solution. It seems to me that it's
> also the most logical for ada?

Be careful. Ada community is deeply divided on this issue! (:-)) There are
people who depreciate use of use. Sorry for an unintended pun. (:-)) I am
not among them.

> Since we're forced to follow the rules
> of strong typing, then it's only logical to have different subroutines
> for each type. There are only less than ten subroutines in my linked
> lists package, so renaming then was easy. Besides, the program becomes
> clearer (in the end, that's what ada encourages us to do, right?) --
> now, for example, we can use a word_walk subroutine for word lists, and
> eg. node_walk for all the others. Agreed, a good idea.

Though it is probably not your case, but just to complete the picture...
Ada supports implementation through renaming. For example, the following is
legal:

package Foo is
   type Word is ...
   procedure Insert (...);

private
   package Baz is new Bar (Word, ...); -- They won't see it
   procedure Insert (...) renames Baz.Insert;
      -- Insert is implemented through renaming of a procedure
   ...
end Foo;

This way you can hide the fact of instantiation of Bar and that Insert is
implemented in Bar. Unfortunately it does not work with types. There is no
type renaming in Ada. Subtyping and deriving a new type sometimes can do
the trick, sometimes not.

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



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

* Re: advice on package design
  2005-03-07 21:08 ` Dmitry A. Kazakov
  2005-03-08 12:48   ` spambox
@ 2005-03-12 19:57   ` Robert A Duff
  2005-03-12 20:45     ` Dmitry A. Kazakov
  1 sibling, 1 reply; 28+ messages in thread
From: Robert A Duff @ 2005-03-12 19:57 UTC (permalink / raw)


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

> 3. You can rename parts of contents of Baz in Foo:
> 
> package Foo is
>    type Word is ...
>    package Baz is new Bar (Word, ...);
> 
>    subtype Linked_List is Baz.Linked_List; -- "renaming" of a type
>    procedure Insert (...) renames Baz.Insert; -- renaming of a procedure
>    ...
> end Foo;

One variation on that is to declare a derived type:

    type Linked_List is new Baz.Linked_List;

This causes all of the primitive subprograms of Baz.Linked_List to be
inherited -- implicitly declared in Foo.  But for anything that's not a
primitive subprograms, you have to write some wrapper code by hand.

If I were [re]designing Ada, I would make 'use' transitive.  That is,
if Foo says "use Linked_List" that would make the contents of
Linked_List directly visible in Foo, and anything that says "use Foo;"
would have direct visibility on everything directly visible in Foo --
not just the things *declared* in Foo.  Or something like that.

It really is a flaw in Ada that you can't easily re-export stuff.
The renaming solution is bad because it is verbose, and therefore
error prone.  I still remember a bug I fixed many years ago:

    function "and"(...) return ... renames Something."and";
    function "or"(...) return ... renames Something."or";
    function "xor"(...) return ... renames Something."or";

The problem is that renaming is *not* usually used to rename things --
it is used to import them (with the *same* name) into a different
scope.  Given that mindset, it took me a *long* time to notice the bug
in the above.  The symptom was that xor was behaving strangely in some
faraway place.

That was Ada 83.  In Ada 95, "use type" would have prevented the
problem.

- Bob



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

* Re: advice on package design
  2005-03-12 19:57   ` Robert A Duff
@ 2005-03-12 20:45     ` Dmitry A. Kazakov
  2005-03-12 21:59       ` Robert A Duff
  0 siblings, 1 reply; 28+ messages in thread
From: Dmitry A. Kazakov @ 2005-03-12 20:45 UTC (permalink / raw)


On 12 Mar 2005 14:57:17 -0500, Robert A Duff wrote:

> If I were [re]designing Ada, I would make 'use' transitive. That is,
> if Foo says "use Linked_List" that would make the contents of
> Linked_List directly visible in Foo, and anything that says "use Foo;"
> would have direct visibility on everything directly visible in Foo --
> not just the things *declared* in Foo.  Or something like that.

Yes, I really miss it. What about:

   use all <package-name>;

or

  use package <package-name>;

with the effect importing the specification of the package? Also,
differently to use, it should prevent hiding. So:

package A is
   Foo (X : Integer);
end A;

with A;
package B is
   use all A;
   Foo (X : out Integer); -- Illegal, can't hide A.Foo
end B;

It could be also used for testing if a child package is parent-friendly:

package Foo.Child is
   ... -- Lot of stuff
private
   use all Foo; -- Fails if Child hides something in Foo
   ...
end Foo;

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



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

* Re: advice on package design
  2005-03-12 20:45     ` Dmitry A. Kazakov
@ 2005-03-12 21:59       ` Robert A Duff
  2005-03-13  9:23         ` Dmitry A. Kazakov
  0 siblings, 1 reply; 28+ messages in thread
From: Robert A Duff @ 2005-03-12 21:59 UTC (permalink / raw)


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

> On 12 Mar 2005 14:57:17 -0500, Robert A Duff wrote:
> 
> > If I were [re]designing Ada, I would make 'use' transitive. That is,
> > if Foo says "use Linked_List" that would make the contents of
> > Linked_List directly visible in Foo, and anything that says "use Foo;"
> > would have direct visibility on everything directly visible in Foo --
> > not just the things *declared* in Foo.  Or something like that.
> 
> Yes, I really miss it. What about:
> 
>    use all <package-name>;
> 
> or
> 
>   use package <package-name>;
> 
> with the effect importing the specification of the package? Also,
> differently to use, it should prevent hiding. So:
> 
> package A is
>    Foo (X : Integer);
> end A;
> 
> with A;
> package B is
>    use all A;
>    Foo (X : out Integer); -- Illegal, can't hide A.Foo
> end B;

Well, I happen to think that *all* hiding is evil.
I would make the above legal, but all calls to Foo would be ambiguous,
and therefore illegal.

Ichbiah defined "Beaujolais Effect" to mean that if you add a subprogram
to a use-d package, and that causes a legal program to change it's
meaning (to another legal program) you have a Beaujolais Effect.
And Beaujolais Effects are bad.  Ada 83 had some Beaujolais Effects,
but they were pretty obscure, and we eliminated them in Ada 95.

But I think he should have extended that definition to include all
visibility, not just use-visibility.  So a local X should not hide a
use-visible X, but be ambiguous with it.  And similar rules for record
(extension) components.

In Pascal, a 'with' statement opens up the visibility of the record
components, and if 'with R' is nested within some place where X is
declared, then record component X hides that X.  That's bad, and Ichbiah
wisely designed 'use' clauses to not do that bad thing.  The trick is to
avoid "preference rules", where one decl takes precedence over another.
But hiding is exactly a preference rule (preferring the inner one over
an outer one, or preferring an outer one over a use-visible one).

C++ is rife with Beaujolais Effects.

- Bob

P.S. The reason for the name is that Ichbiah offered to give a bottle
(or was it a case?) of Beaujolais to anybody who could find such a
problem in Ada.



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

* Re: advice on package design
  2005-03-12 21:59       ` Robert A Duff
@ 2005-03-13  9:23         ` Dmitry A. Kazakov
  2005-03-16 20:41           ` Robert A Duff
  0 siblings, 1 reply; 28+ messages in thread
From: Dmitry A. Kazakov @ 2005-03-13  9:23 UTC (permalink / raw)


On 12 Mar 2005 16:59:26 -0500, Robert A Duff wrote:

> "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> writes:
> 
>> On 12 Mar 2005 14:57:17 -0500, Robert A Duff wrote:
>> 
>>> If I were [re]designing Ada, I would make 'use' transitive. That is,
>>> if Foo says "use Linked_List" that would make the contents of
>>> Linked_List directly visible in Foo, and anything that says "use Foo;"
>>> would have direct visibility on everything directly visible in Foo --
>>> not just the things *declared* in Foo.  Or something like that.
>> 
>> Yes, I really miss it. What about:
>> 
>>    use all <package-name>;
>> 
>> or
>> 
>>   use package <package-name>;
>> 
>> with the effect importing the specification of the package? Also,
>> differently to use, it should prevent hiding. So:
>> 
>> package A is
>>    Foo (X : Integer);
>> end A;
>> 
>> with A;
>> package B is
>>    use all A;
>>    Foo (X : out Integer); -- Illegal, can't hide A.Foo
>> end B;
> 
> Well, I happen to think that *all* hiding is evil.
> I would make the above legal, but all calls to Foo would be ambiguous,
> and therefore illegal.

That makes sense, but it might be difficult for the programmer to resolve
the problem. Somewhere in 20th child package of B he calls Foo and, oops,
it is ambiguous. I think it is better to keep all scopes clean from the
beginning.

> Ichbiah defined "Beaujolais Effect" to mean that if you add a subprogram
> to a use-d package, and that causes a legal program to change it's
> meaning (to another legal program) you have a Beaujolais Effect.
> And Beaujolais Effects are bad.  Ada 83 had some Beaujolais Effects,
> but they were pretty obscure, and we eliminated them in Ada 95.
> 
> But I think he should have extended that definition to include all
> visibility, not just use-visibility.  So a local X should not hide a
> use-visible X, but be ambiguous with it.

Yes. Even in the cases like:

declare
   I : Integer;
begin
   for I in A'Range loop
      A (I) := 0;
   end loop;

However I'd like to get error message one line above than you.

> And similar rules for record
> (extension) components.

I think that this case is not so simple. IMO record components should be
treated as primitive operations (getter/setter pair). As such they could be
overridden upon extension. At the same time some of the components might be
declared as a kind of "class-wide". For these the rules preventing hiding
should apply.

> In Pascal, a 'with' statement opens up the visibility of the record
> components, and if 'with R' is nested within some place where X is
> declared, then record component X hides that X.  That's bad, and Ichbiah
> wisely designed 'use' clauses to not do that bad thing.  The trick is to
> avoid "preference rules", where one decl takes precedence over another.
> But hiding is exactly a preference rule (preferring the inner one over
> an outer one, or preferring an outer one over a use-visible one).

Alas, but preference rules cannot be avoided. Overriding is just that
preference.

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



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

* Re: advice on package design
  2005-03-13  9:23         ` Dmitry A. Kazakov
@ 2005-03-16 20:41           ` Robert A Duff
  2005-03-17 10:22             ` Dmitry A. Kazakov
  0 siblings, 1 reply; 28+ messages in thread
From: Robert A Duff @ 2005-03-16 20:41 UTC (permalink / raw)


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

> On 12 Mar 2005 16:59:26 -0500, Robert A Duff wrote:
> 
> > "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> writes:
> > 
> >> On 12 Mar 2005 14:57:17 -0500, Robert A Duff wrote:
> >> 
> >>> If I were [re]designing Ada, I would make 'use' transitive. That is,
> >>> if Foo says "use Linked_List" that would make the contents of
> >>> Linked_List directly visible in Foo, and anything that says "use Foo;"
> >>> would have direct visibility on everything directly visible in Foo --
> >>> not just the things *declared* in Foo.  Or something like that.
> >> 
> >> Yes, I really miss it. What about:
> >> 
> >>    use all <package-name>;
> >> 
> >> or
> >> 
> >>   use package <package-name>;
> >> 
> >> with the effect importing the specification of the package? Also,
> >> differently to use, it should prevent hiding. So:
> >> 
> >> package A is
> >>    Foo (X : Integer);
> >> end A;
> >> 
> >> with A;
> >> package B is
> >>    use all A;
> >>    Foo (X : out Integer); -- Illegal, can't hide A.Foo
> >> end B;
> > 
> > Well, I happen to think that *all* hiding is evil.
> > I would make the above legal, but all calls to Foo would be ambiguous,
> > and therefore illegal.
> 
> That makes sense, but it might be difficult for the programmer to resolve
> the problem. Somewhere in 20th child package of B he calls Foo and, oops,
> it is ambiguous. I think it is better to keep all scopes clean from the
> beginning.

I see your point, but I think that can be dealt with by having good
error messages.  In any language that allows overloading, there are
going to potential ambiguities, and when they happen, I expect more from
the compiler than just "error: I'm confused".

In Ada, if you have:

procedure Foo(X: Integer);
procedure Foo(Y: Integer);

in the same package, it's illegal.  But if they're in two different
packages, both use-visible, then it's legal, and you can call them
using named notation: Foo(X => 1) vs. Foo(Y => 1).  That seems odd
to me.  Should that use clause be illegal?

I would prefer to allow declaring of anything, but then declare
potentially-confusing uses ambiguous.  And don't make the
overload-resolution rules too "smart", so they make many things
ambiguous (and therefore illegal).  And rely on compilers to give useful
error messages for ambiguity.

That seems better than trying to prevent possibly-ambiguous things ahead
of time, at the declaration points.

> > Ichbiah defined "Beaujolais Effect" to mean that if you add a subprogram
> > to a use-d package, and that causes a legal program to change it's
> > meaning (to another legal program) you have a Beaujolais Effect.
> > And Beaujolais Effects are bad.  Ada 83 had some Beaujolais Effects,
> > but they were pretty obscure, and we eliminated them in Ada 95.
> > 
> > But I think he should have extended that definition to include all
> > visibility, not just use-visibility.  So a local X should not hide a
> > use-visible X, but be ambiguous with it.
> 
> Yes. Even in the cases like:
> 
> declare
>    I : Integer;
> begin
>    for I in A'Range loop
>       A (I) := 0;
>    end loop;

Yes, I agree.

> However I'd like to get error message one line above than you.

OK, but I'm willing to trust the compiler to do that (i.e. say,
"I" is ambiguous here, because one-line-above you have an I that
conflicts with the I three-lines-above).

> > And similar rules for record
> > (extension) components.
> 
> I think that this case is not so simple. IMO record components should be
> treated as primitive operations (getter/setter pair). As such they could be
> overridden upon extension. At the same time some of the components might be
> declared as a kind of "class-wide". For these the rules preventing hiding
> should apply.
> 
> > In Pascal, a 'with' statement opens up the visibility of the record
> > components, and if 'with R' is nested within some place where X is
> > declared, then record component X hides that X.  That's bad, and Ichbiah
> > wisely designed 'use' clauses to not do that bad thing.  The trick is to
> > avoid "preference rules", where one decl takes precedence over another.
> > But hiding is exactly a preference rule (preferring the inner one over
> > an outer one, or preferring an outer one over a use-visible one).
> 
> Alas, but preference rules cannot be avoided. Overriding is just that
> preference.

I don't see why.  Overriding is different -- the old thing goes away.
It's explicit, so it's OK.  If we had this language:

declare
   I : Integer;
begin
   hide I;     <<<<<<<<<<<<<<<<<<<<<<<<
   for I in A'Range loop
      A (I) := 0;
   end loop;

then the hiding would be OK, IMHO.

- Bob



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

* Re: advice on package design
  2005-03-16 20:41           ` Robert A Duff
@ 2005-03-17 10:22             ` Dmitry A. Kazakov
  2005-03-17 14:04               ` Robert A Duff
  0 siblings, 1 reply; 28+ messages in thread
From: Dmitry A. Kazakov @ 2005-03-17 10:22 UTC (permalink / raw)


On 16 Mar 2005 15:41:55 -0500, Robert A Duff wrote:

> "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> writes:
> 
>> On 12 Mar 2005 16:59:26 -0500, Robert A Duff wrote:
>> 
>>> "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> writes:
>> 
>> That makes sense, but it might be difficult for the programmer to resolve
>> the problem. Somewhere in 20th child package of B he calls Foo and, oops,
>> it is ambiguous. I think it is better to keep all scopes clean from the
>> beginning.
> 
> I see your point, but I think that can be dealt with by having good
> error messages.  In any language that allows overloading, there are
> going to potential ambiguities, and when they happen, I expect more from
> the compiler than just "error: I'm confused".
> 
> In Ada, if you have:
> 
> procedure Foo(X: Integer);
> procedure Foo(Y: Integer);
> 
> in the same package, it's illegal.  But if they're in two different
> packages, both use-visible, then it's legal, and you can call them
> using named notation: Foo(X => 1) vs. Foo(Y => 1).  That seems odd
> to me.  Should that use clause be illegal?

Yes, at least "transitive use" should be. Otherwise, overloading of two
Foo's should be allowed (which would be bad).

Good error messages might be OK from the perspective of a package user. But
from the package designer's one it is not enough, when he wants to make his
packages "use"-friendly, especially "transitive use"-friendly.

> ...  And don't make the
> overload-resolution rules too "smart", so they make many things
> ambiguous (and therefore illegal).

I definitely agree with this.

>> Alas, but preference rules cannot be avoided. Overriding is just that
>> preference.
> 
> I don't see why.  Overriding is different -- the old thing goes away.
> It's explicit, so it's OK.

Presently it is not explicit. Syntactically overriding is undistinguishable
from declaring a new operation. This is IMO bad. It should be sort of:

procedure Override (X : Object) is ????;
   -- Overriding intended, fails if base types have no primitive Override
procedure Override (X : Object);
   -- Overloading intended, fails if it hides any other Override

>  If we had this language:
> 
> declare
>    I : Integer;
> begin
>    hide I;     <<<<<<<<<<<<<<<<<<<<<<<<
>    for I in A'Range loop
>       A (I) := 0;
>    end loop;
> 
> then the hiding would be OK, IMHO.

I agree with the idea, but I think that hiding should appear in a
declarative part. Less probably it should also qualify the thing being
hidden (like renames does):

declare
   I : Integer;
begin
   declare
      I : Integer is null; -- Know me, if you want to get rid of me!
   begin
      for I in A'Range loop
         A (I) := 0;
      end loop;

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



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

* Re: advice on package design
  2005-03-17 10:22             ` Dmitry A. Kazakov
@ 2005-03-17 14:04               ` Robert A Duff
  2005-03-17 15:59                 ` Dmitry A. Kazakov
  2005-03-17 23:23                 ` Randy Brukardt
  0 siblings, 2 replies; 28+ messages in thread
From: Robert A Duff @ 2005-03-17 14:04 UTC (permalink / raw)


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

> Presently it is not explicit. Syntactically overriding is undistinguishable
> from declaring a new operation. This is IMO bad. It should be sort of:
> 
> procedure Override (X : Object) is ????;
>    -- Overriding intended, fails if base types have no primitive Override
> procedure Override (X : Object);
>    -- Overloading intended, fails if it hides any other Override

I think Ada 2005 has something like that.

> I agree with the idea, but I think that hiding should appear in a
> declarative part. Less probably it should also qualify the thing being
> hidden (like renames does):

I don't believe in declarative parts.  I think declare/begin/end
should not be required just because I want to declare a constant
or something.

- Bob



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

* Re: advice on package design
  2005-03-17 14:04               ` Robert A Duff
@ 2005-03-17 15:59                 ` Dmitry A. Kazakov
  2005-03-17 19:10                   ` Robert A Duff
  2005-03-17 23:23                 ` Randy Brukardt
  1 sibling, 1 reply; 28+ messages in thread
From: Dmitry A. Kazakov @ 2005-03-17 15:59 UTC (permalink / raw)


On 17 Mar 2005 09:04:26 -0500, Robert A Duff wrote:

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

>> I agree with the idea, but I think that hiding should appear in a
>> declarative part. Less probably it should also qualify the thing being
>> hidden (like renames does):
> 
> I don't believe in declarative parts.  I think declare/begin/end
> should not be required just because I want to declare a constant
> or something.

Disagree. I don't like the idea of something changing its meaning within
the same scope, even if that is non-existing -> exiting. I have to tolerate
this in a declarative region because probably there is no better way to do
it. But I don't want to let it leak out of the quarantine zone.

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



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

* Re: advice on package design
  2005-03-17 15:59                 ` Dmitry A. Kazakov
@ 2005-03-17 19:10                   ` Robert A Duff
  2005-03-17 19:47                     ` Martin Dowie
  2005-03-17 20:48                     ` Dmitry A. Kazakov
  0 siblings, 2 replies; 28+ messages in thread
From: Robert A Duff @ 2005-03-17 19:10 UTC (permalink / raw)


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

> On 17 Mar 2005 09:04:26 -0500, Robert A Duff wrote:
> 
> > "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> writes:
> 
> >> I agree with the idea, but I think that hiding should appear in a
> >> declarative part. Less probably it should also qualify the thing being
> >> hidden (like renames does):
> > 
> > I don't believe in declarative parts.  I think declare/begin/end
> > should not be required just because I want to declare a constant
> > or something.
> 
> Disagree. I don't like the idea of something changing its meaning within
> the same scope, even if that is non-existing -> exiting. I have to tolerate
> this in a declarative region because probably there is no better way to do
> it. But I don't want to let it leak out of the quarantine zone.

I don't understand what you mean about "changing meaning".
I just want to be able to write things like:

    for I in A'Range loop
        This_Component: constant T := A(I);
        ... several uses of This_Component
    end loop;

without adding three extra (useless) lines of code.
And we agreed that if there's another thing called This_Component,
this This_Component should not hide that This_Component.

The "hide" command I proposed is of dubious value, but if it were
allowed, I wouldn't mind restricting it to whole procedures
or something.  Or not.  But either way, I don't like the separation
of "declarations" and "statements".

- Bob



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

* Re: advice on package design
  2005-03-17 19:10                   ` Robert A Duff
@ 2005-03-17 19:47                     ` Martin Dowie
  2005-03-17 20:55                       ` Robert A Duff
  2005-03-17 20:48                     ` Dmitry A. Kazakov
  1 sibling, 1 reply; 28+ messages in thread
From: Martin Dowie @ 2005-03-17 19:47 UTC (permalink / raw)


Robert A Duff wrote:
> "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> writes:
> 
> 
>>On 17 Mar 2005 09:04:26 -0500, Robert A Duff wrote:
>>
>>
>>>"Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> writes:
>>
>>>>I agree with the idea, but I think that hiding should appear in a
>>>>declarative part. Less probably it should also qualify the thing being
>>>>hidden (like renames does):
>>>
>>>I don't believe in declarative parts.  I think declare/begin/end
>>>should not be required just because I want to declare a constant
>>>or something.
>>
>>Disagree. I don't like the idea of something changing its meaning within
>>the same scope, even if that is non-existing -> exiting. I have to tolerate
>>this in a declarative region because probably there is no better way to do
>>it. But I don't want to let it leak out of the quarantine zone.
> 
> 
> I don't understand what you mean about "changing meaning".
> I just want to be able to write things like:
> 
>     for I in A'Range loop
>         This_Component: constant T := A(I);
>         ... several uses of This_Component
>     end loop;
> 
> without adding three extra (useless) lines of code.
> And we agreed that if there's another thing called This_Component,
> this This_Component should not hide that This_Component.
> 
> The "hide" command I proposed is of dubious value, but if it were
> allowed, I wouldn't mind restricting it to whole procedures
> or something.  Or not.  But either way, I don't like the separation
> of "declarations" and "statements".

The bit about declarations and statements I don't like is where 
exceptions occur...

e.g.

with Ada.Text_IO; use Ada.Text_IO;
procedure Main is
    function Foo (I : Integer) return Integer is
       I2 : Integer := I + 1;
    begin
       return I2;
    exception
       when others =>
          return 0;
    end Foo;

    procedure Bar is
       I : Integer;
    begin
       I := Foo (Integer'Last);
    end Bar;
begin
    Bar;
exception
    when others =>
       Put_Line ("Rats... why not catch it in 'Foo'?");
end Main;

I've never understood why this needs to be this way...

Cheers

-- Martin



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

* Re: advice on package design
  2005-03-17 19:10                   ` Robert A Duff
  2005-03-17 19:47                     ` Martin Dowie
@ 2005-03-17 20:48                     ` Dmitry A. Kazakov
  2005-03-17 21:26                       ` Robert A Duff
  1 sibling, 1 reply; 28+ messages in thread
From: Dmitry A. Kazakov @ 2005-03-17 20:48 UTC (permalink / raw)


On 17 Mar 2005 14:10:11 -0500, Robert A Duff wrote:

> "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> writes:
> 
>> On 17 Mar 2005 09:04:26 -0500, Robert A Duff wrote:
>> 
>>> "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> writes:
>> 
>>>> I agree with the idea, but I think that hiding should appear in a
>>>> declarative part. Less probably it should also qualify the thing being
>>>> hidden (like renames does):
>>> 
>>> I don't believe in declarative parts.  I think declare/begin/end
>>> should not be required just because I want to declare a constant
>>> or something.
>> 
>> Disagree. I don't like the idea of something changing its meaning within
>> the same scope, even if that is non-existing -> exiting. I have to tolerate
>> this in a declarative region because probably there is no better way to do
>> it. But I don't want to let it leak out of the quarantine zone.
> 
> I don't understand what you mean about "changing meaning".
> I just want to be able to write things like:
> 
>     for I in A'Range loop

Until here --> This_Component = nothing

>         This_Component: constant T := A(I);

Below --> This_Component = constant T

>         ... several uses of This_Component
>     end loop;
> 
> without adding three extra (useless) lines of code.
> And we agreed that if there's another thing called This_Component,
> this This_Component should not hide that This_Component.

Maybe it is a matter of taste, but it is one of things in C++ which I hate
the most. It is very difficult (to me) to understand a program with mixed
declarations and statements.

If you care about extra lines, then I'd propose:

   for I in A'Range -- All declarations be between for...loop
      This_Component: constant T := A(I);
   loop
      ... several uses of This_Component
   end loop;
 
> The "hide" command I proposed is of dubious value, but if it were
> allowed, I wouldn't mind restricting it to whole procedures
> or something.  Or not.  But either way, I don't like the separation
> of "declarations" and "statements".

I like the separation very much.

But let's consider getting rid of declarations. Wouldn't it be fake anyway?
You know that better than me, but I suppose the compiler will need to move
everything to the beginning of the closest scope. Otherwise:

   if Halt(x) then
     declare A;
   end if;
   Foo (A); -- Would it be legal?

   T := (1, 2, 4, declare X := 9, others => declare Y := 10);
      -- What would be this? How many Y's could be here?

So I see no advantage in this.

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



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

* Re: advice on package design
  2005-03-17 19:47                     ` Martin Dowie
@ 2005-03-17 20:55                       ` Robert A Duff
  2005-03-17 21:14                         ` Marius Amado Alves
  2005-03-18  9:38                         ` Martin Dowie
  0 siblings, 2 replies; 28+ messages in thread
From: Robert A Duff @ 2005-03-17 20:55 UTC (permalink / raw)


Martin Dowie <martin.dowie@btopenworld.com> writes:

> The bit about declarations and statements I don't like is where
> exceptions occur...
> 
> e.g.
> 
> with Ada.Text_IO; use Ada.Text_IO;
> procedure Main is
>     function Foo (I : Integer) return Integer is
>        I2 : Integer := I + 1;
>     begin
>        return I2;
>     exception
>        when others =>
>           return 0;
>     end Foo;
> 
>     procedure Bar is
>        I : Integer;
>     begin
>        I := Foo (Integer'Last);
>     end Bar;
> begin
>     Bar;
> exception
>     when others =>
>        Put_Line ("Rats... why not catch it in 'Foo'?");
> end Main;
> 
> I've never understood why this needs to be this way...

Because I2 is visible in the handler.  But it's declaration is never
successfully elaborated, so it does not exist!

procedure P is
    X: Integer := F(...); -- raises Some_Error
    Y: Integer := 123;
begin
    ...
exception
    when Some_Error =>
        Put(X); Put(Y); -- What are the values of X and Y here?
end P;

Or worse:

procedure P is
    X: Integer := F(...); -- raises Some_Error
    Y: String := Read_Line;
begin
    ...
exception
    when Some_Error =>
        Put(Y);
end P;

At the point of "Put(Y);", not even the bounds of Y have been
calculated, much less its value.

But you're right -- the syntax is somewhat confusing, since the
exception handler *looks* like it handles exceptions for the whole
procedure.  Another reason to get rid of declarative parts.  ;-)

I think a better syntax would a separate statement -- like try/catch in
Java.  That makes it clear what region of code is protected by
the handler.

- Bob



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

* Re: advice on package design
  2005-03-17 20:55                       ` Robert A Duff
@ 2005-03-17 21:14                         ` Marius Amado Alves
  2005-03-18  9:31                           ` Martin Dowie
  2005-03-18  9:38                         ` Martin Dowie
  1 sibling, 1 reply; 28+ messages in thread
From: Marius Amado Alves @ 2005-03-17 21:14 UTC (permalink / raw)
  Cc: comp.lang.ada

>> with Ada.Text_IO; use Ada.Text_IO;
>> procedure Main is
>>     function Foo (I : Integer) return Integer is
>>        I2 : Integer := I + 1;
>>     begin
>>        return I2;
>>     exception
>>        when others =>
>>           return 0;
>>     end Foo;
...
>> exception
>>     when others =>
>>        Put_Line ("Rats... why not catch it in 'Foo'?");
>>

Bob told you why. I'll tell you how, just in case. Rewrite Foo thus:

  function Foo (I : Integer) return Integer is
  begin
     declare
        I2 : Integer := I + 1;
     begin
        return I2;
     end;
  exception
     when others =>
        return 0;
  end Foo;

(Of course this how follows from the why. I2 is no longer visible in 
the handler.)




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

* Re: advice on package design
  2005-03-17 20:48                     ` Dmitry A. Kazakov
@ 2005-03-17 21:26                       ` Robert A Duff
  2005-03-18  3:06                         ` Jared
  2005-03-18 10:00                         ` Dmitry A. Kazakov
  0 siblings, 2 replies; 28+ messages in thread
From: Robert A Duff @ 2005-03-17 21:26 UTC (permalink / raw)


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

> I like the separation very much.

You're not alone.

Part of my reason is that declarations in Ada execute code at run time.
In other languages (e.g. Pascal) there is a strict separation:
declarations declare, statements execute, so it makes sense to have
a syntactic line drawn between the two.  But in Ada, the decls can
do real work, so the line drawn by the "begin" is misleading.
In some cases, the decls do *all* the work, especially when you're
working with Strings, where the length usually depends on the 
initial value.  Then the procedure body says "null;", meaning
"this does nothing", which is a lie.

Sometimes you end up with long chains of deeply nested declare blocks:

    function F(...) return String is
        A: String := ...;
    begin
        Do_Something(A);
        declare
            B: constant String := G(A);    
        begin
            ... and so on ..................................
                                                            declare
                                                                Z: String ...
                                                            begin
                                                                return Z;

Yuck.

Another thing that I trip over once in a while is I want a *statement*
near the beginning of a package body.  It is important that it execute
at the point, because following code (yes, decls are code!) depends
on it.  You can say this:

    package body P is
        package Defeat_Annoying_Syntax_Rules is end;
        package body Defeat_Annoying_Syntax_Rules is
        begin
            Initialize_Database;
        end;

        ... -- 200 lines of code
    end P;

But that seems less readable (to me) than just writing
"Initialize_Database;" where I want it to happen.
So a rule (requiring "declare") that is intended to increase
readability actually decreases it.

And if you want a statement in a package *spec* (say, to initialize
something), it's far worse.  There's no direct workaround; you have to
totally rearrange the structure of your design in order to get around
a silly syntax rule.

It seems to me that:

    Mumble: T := Blah(...);

should be precisely equivalent to:

    Mumble: T;
    Mumble := Blah(...);

which should be precisely equivalent to:

    Mumble: T;
    Blah(Mumble);

(where in the last example we replace function Blah with a procedure
with an 'out' parameter).  The programmer should be able to switch from
one of these forms to another, without changing the semantics of the
program.  But the syntax rules require stirring the code around
when you make these changes.

(Another reason Ada does not have the above desirable property is that
function results of subtype String behave differently from 'out'
parameters of subtype String.  I don't like that.)

> But let's consider getting rid of declarations. Wouldn't it be fake anyway?
> You know that better than me, but I suppose the compiler will need to move
> everything to the beginning of the closest scope. Otherwise:
> 
>    if Halt(x) then
>      declare A;
>    end if;
>    Foo (A); -- Would it be legal?

By "declare A;" I assume you mean something like "A: Integer;".
No, A would not be visible at the call to Foo -- it would be
just like putting a declare block inside the if statement
in the current language.  I'm not proposing anything different
in the semantics, here -- just a minor syntax change.

>    T := (1, 2, 4, declare X := 9, others => declare Y := 10);
>       -- What would be this? How many Y's could be here?

No, I don't propose to allow mixing of decls and expressions!
Just decls and statements.  Decls don't return values.

> So I see no advantage in this.

Convinced?  Partly convinced?  ;-)

- Bob



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

* Re: advice on package design
  2005-03-17 14:04               ` Robert A Duff
  2005-03-17 15:59                 ` Dmitry A. Kazakov
@ 2005-03-17 23:23                 ` Randy Brukardt
  1 sibling, 0 replies; 28+ messages in thread
From: Randy Brukardt @ 2005-03-17 23:23 UTC (permalink / raw)


"Robert A Duff" <bobduff@shell01.TheWorld.com> wrote in message
news:wccpsxy1i8l.fsf@shell01.TheWorld.com...
> "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> writes:
>
> > Presently it is not explicit. Syntactically overriding is
undistinguishable
> > from declaring a new operation. This is IMO bad. It should be sort of:
> >
> > procedure Override (X : Object) is ????;
> >    -- Overriding intended, fails if base types have no primitive
Override
> > procedure Override (X : Object);
> >    -- Overloading intended, fails if it hides any other Override
>
> I think Ada 2005 has something like that.

Yes.

overriding
procedure Override (X : Object);
    -- Overriding intended, fails if base types have no primitive Override

not overriding
procedure Override (X : Object);
    -- Overloading intended, fails if it hides any other Override

Note the suggested indentation. If you have neither, you revert to the Ada
95 rules. That turns out to be necessary in some cases for private types and
for generics.

This would have saved us a lot of headaches constructing Claw.

                      Randy.






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

* Re: advice on package design
  2005-03-17 21:26                       ` Robert A Duff
@ 2005-03-18  3:06                         ` Jared
  2005-03-18 10:00                         ` Dmitry A. Kazakov
  1 sibling, 0 replies; 28+ messages in thread
From: Jared @ 2005-03-18  3:06 UTC (permalink / raw)


Robert A Duff <bobduff@shell01.TheWorld.com> wrote in
news:wccsm2u6k1t.fsf@shell01.TheWorld.com: 

> But that seems less readable (to me) than just writing
> "Initialize_Database;" where I want it to happen.
> So a rule (requiring "declare") that is intended to increase
> readability actually decreases it.
> 
> And if you want a statement in a package *spec* (say, to initialize
> something), it's far worse.  There's no direct workaround; you have to
> totally rearrange the structure of your design in order to get around
> a silly syntax rule.

Is this terribly common, though?  I'm just a hobbyist, so when I run
across something like that, I just assume I have a bad design.

> It seems to me that:
> 
>     Mumble: T := Blah(...);
> 
> should be precisely equivalent to:
> 
>     Mumble: T;
>     Mumble := Blah(...);
> 
> which should be precisely equivalent to:
> 
>     Mumble: T;
>     Blah(Mumble);
> 
> (where in the last example we replace function Blah with a procedure
> with an 'out' parameter).  The programmer should be able to switch
> from one of these forms to another, without changing the semantics of
> the program.  But the syntax rules require stirring the code around
> when you make these changes.
> 
> (Another reason Ada does not have the above desirable property is that
> function results of subtype String behave differently from 'out'
> parameters of subtype String.  I don't like that.)

This is the case for all unconstrained array types.  In order to use
unconstrained variables, we three distasteful choices.  Invoke the
entire object mechanism for a single controlled type, fiddle with
access types and hope not to drop any references, or create code that
looks like:

>     function F(...) return String is
>         A: String := ...;
>     begin
>         Do_Something(A);
>         declare
>             B: constant String := G(A);    
>         begin
>             ... and so on ..................................
>                                                             declare
>                                                                 Z:
>                               String ... 
>                                                             begin
>                                                                 return
>                               Z; 
> 
> Yuck.

There's always Ada.Strings.Unbounded.Unbounded_String, but that's
only good for character arrays.


Incidentally, I'm just a hobbyist, so I don't know about this; how
common is it in production code to declare a controlled type just
to guarantee uniqueness of reference?  Of those, how many are used
for arrays?



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

* Re: advice on package design
  2005-03-17 21:14                         ` Marius Amado Alves
@ 2005-03-18  9:31                           ` Martin Dowie
  0 siblings, 0 replies; 28+ messages in thread
From: Martin Dowie @ 2005-03-18  9:31 UTC (permalink / raw)


Marius Amado Alves wrote:
> Bob told you why. I'll tell you how, just in case. Rewrite Foo thus:
>
>   function Foo (I : Integer) return Integer is
>   begin
>      declare
>         I2 : Integer := I + 1;
>      begin
>         return I2;
>      end;
>   exception
>      when others =>
>         return 0;
>   end Foo;
>
> (Of course this how follows from the why. I2 is no longer visible in
> the handler.)

Thanks - I'm afraid I've already been through the pain of doing just
this on several dozen function on my first Ada83 program when I
discovered this little 'design feature' :-)

Cheers

-- Martin







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

* Re: advice on package design
  2005-03-17 20:55                       ` Robert A Duff
  2005-03-17 21:14                         ` Marius Amado Alves
@ 2005-03-18  9:38                         ` Martin Dowie
  2005-03-21 16:19                           ` Robert A Duff
  1 sibling, 1 reply; 28+ messages in thread
From: Martin Dowie @ 2005-03-18  9:38 UTC (permalink / raw)


Robert A Duff wrote:
>> I've never understood why this needs to be this way...
>
> Because I2 is visible in the handler.  But it's declaration is never
> successfully elaborated, so it does not exist!
>
> procedure P is
>     X: Integer := F(...); -- raises Some_Error
>     Y: Integer := 123;
> begin
>     ...
> exception
>     when Some_Error =>
>         Put(X); Put(Y); -- What are the values of X and Y here?
> end P;

And we couldn't have come up with some form of words to get
round that?!?!?!?! :-)







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

* Re: advice on package design
  2005-03-17 21:26                       ` Robert A Duff
  2005-03-18  3:06                         ` Jared
@ 2005-03-18 10:00                         ` Dmitry A. Kazakov
  2005-03-21 16:17                           ` Robert A Duff
  1 sibling, 1 reply; 28+ messages in thread
From: Dmitry A. Kazakov @ 2005-03-18 10:00 UTC (permalink / raw)


On 17 Mar 2005 16:26:22 -0500, Robert A Duff wrote:

> "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> writes:
> 
>> I like the separation very much.
> 
> You're not alone.
> 
> Part of my reason is that declarations in Ada execute code at run time.
> In other languages (e.g. Pascal) there is a strict separation:
> declarations declare, statements execute, so it makes sense to have
> a syntactic line drawn between the two.  But in Ada, the decls can
> do real work, so the line drawn by the "begin" is misleading.

Yes, indirectly they do. For the following body this is the code that never
fail. Similarly, each "end;" is in fact also a code which again never fails
(for the body above). I can buy this as modularization (can I sell it so
too? (:-))

> In some cases, the decls do *all* the work, especially when you're
> working with Strings, where the length usually depends on the 
> initial value.  Then the procedure body says "null;", meaning
> "this does nothing", which is a lie.

Yes. A similar case is:

if 0 = Some_Win_API (...) then
   null; -- Don't care about your silly return codes!
end if;

> Sometimes you end up with long chains of deeply nested declare blocks:
> 
>     function F(...) return String is
>         A: String := ...;
>     begin
>         Do_Something(A);
>         declare
>             B: constant String := G(A);    
>         begin
>             ... and so on ..................................
>                                                             declare
>                                                                 Z: String ...
>                                                             begin
>                                                                 return Z;
> 
> Yuck.

I also dislike unnecessary indentations, but what if exception handling is
needed? Then you have to create blocks anyway.

> It seems to me that:
> 
>     Mumble: T := Blah(...);
> 
> should be precisely equivalent to:
> 
>     Mumble: T;
>     Mumble := Blah(...);

So Mumble is first constructed and then assigned? Would you leave that to
the optimizer? I wouldn't. I prefer a clear distinction between ":=" in
declarations (= in-place construction with parameters) and ":=" in the body
(= assignment).

> which should be precisely equivalent to:
> 
>     Mumble: T;
>     Blah(Mumble);
> 
> (where in the last example we replace function Blah with a procedure
> with an 'out' parameter). The programmer should be able to switch from
> one of these forms to another, without changing the semantics of the
> program.

But that will require switching to heap. I would like to leave that all to
the programmer. Let he be able to *easily* create access types acting as
light-weight handles. Then "out" will be just a handle. Why to overload the
compiler with things it cannot do optimal and require from a programmer an
in-depth understanding of what compiler will do IF?

> But the syntax rules require stirring the code around
> when you make these changes.

> (Another reason Ada does not have the above desirable property is that
> function results of subtype String behave differently from 'out'
> parameters of subtype String.  I don't like that.)

Neither I do. But I think that the difference is rather fundamental. It
appears all over many other places: in object construction, in
discriminated types, in class-wide types etc. For this reason, I would like
to see some general mechanism for evaluation of the constraints
*independently* from the values; and also for forcing the compiler to
evaluate statically known constraints at compile time and to remove them
from all sorts of dopes. So instead of hiding the skeleton in the cupboard
I would openly present it to the public! (:-))

>> But let's consider getting rid of declarations. Wouldn't it be fake anyway?
>> You know that better than me, but I suppose the compiler will need to move
>> everything to the beginning of the closest scope. Otherwise:
>> 
>>    if Halt(x) then
>>      declare A;
>>    end if;
>>    Foo (A); -- Would it be legal?
> 
> By "declare A;" I assume you mean something like "A: Integer;".
> No, A would not be visible at the call to Foo -- it would be
> just like putting a declare block inside the if statement
> in the current language.  I'm not proposing anything different
> in the semantics, here -- just a minor syntax change.

I see. But that will require definition of which parts of which statements
may act as scopes: loop, if alternative, case choice, exception choice
(already so in the "X : others"-kludge), select choice etc. How many pages
of ARM? (:-))

>>    T := (1, 2, 4, declare X := 9, others => declare Y := 10);
>>       -- What would be this? How many Y's could be here?
> 
> No, I don't propose to allow mixing of decls and expressions!
> Just decls and statements.  Decls don't return values.

Once you let them in, then: if I can put declarations among statements, why
cannot I put statements among declarations?

>> So I see no advantage in this.
> 
> Convinced?  Partly convinced?  ;-)

I see the rationale and the problem, but the solution ...

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



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

* Re: advice on package design
  2005-03-18 10:00                         ` Dmitry A. Kazakov
@ 2005-03-21 16:17                           ` Robert A Duff
  2005-03-21 18:16                             ` Dmitry A. Kazakov
  0 siblings, 1 reply; 28+ messages in thread
From: Robert A Duff @ 2005-03-21 16:17 UTC (permalink / raw)


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

> On 17 Mar 2005 16:26:22 -0500, Robert A Duff wrote:
> 
> > "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> writes:
> > 
> >> I like the separation very much.
> > 
> > You're not alone.
> > 
> > Part of my reason is that declarations in Ada execute code at run time.
> > In other languages (e.g. Pascal) there is a strict separation:
> > declarations declare, statements execute, so it makes sense to have
> > a syntactic line drawn between the two.  But in Ada, the decls can
> > do real work, so the line drawn by the "begin" is misleading.
> 
> Yes, indirectly they do. For the following body this is the code that never
> fail. Similarly, each "end;" is in fact also a code which again never fails
> (for the body above). I can buy this as modularization (can I sell it so
> too? (:-))

The problem is that whether something goes above or below the "begin"
depends on all kinds of extraneous factors.  If I want to initialize a
variable with a function, it goes above the line.  If I want to use
a procedure call, or a loop, it goes below the line.  Etc.

> > In some cases, the decls do *all* the work, especially when you're
> > working with Strings, where the length usually depends on the 
> > initial value.  Then the procedure body says "null;", meaning
> > "this does nothing", which is a lie.
> 
> Yes. A similar case is:
> 
> if 0 = Some_Win_API (...) then
>    null; -- Don't care about your silly return codes!
> end if;

It doesn't seem similar to me.  Here, we are explicitly saying that the
"then" part does nothing, and that's a good thing.  (Some other
languages are more error prone in this regard.)  But a procedure that
does a lot of work that happens to be above the "begin" still needs
"null", which seems sort of silly.

> I also dislike unnecessary indentations, but what if exception handling is
> needed? Then you have to create blocks anyway.

I would prefer a separate try/catch-like statement for exception
handling.  Exception handling seems totally unrelated to declaring
things, so I don't see why they should be mixed in the same syntax.
Yes, exception handling requires indentation levels -- seems OK to me.

> > It seems to me that:
> > 
> >     Mumble: T := Blah(...);
> > 
> > should be precisely equivalent to:
> > 
> >     Mumble: T;
> >     Mumble := Blah(...);
> 
> So Mumble is first constructed and then assigned? Would you leave that to
> the optimizer? I wouldn't. I prefer a clear distinction between ":=" in
> declarations (= in-place construction with parameters) and ":=" in the body
> (= assignment).

OK, I take it back.  You're right.  I do not want default-initialization
to happen if there's an explicit initialization.

> Neither I do. But I think that the difference is rather fundamental. It
> appears all over many other places: in object construction, in
> discriminated types, in class-wide types etc. For this reason, I would like
> to see some general mechanism for evaluation of the constraints
> *independently* from the values;

Yes.

>... and also for forcing the compiler to
> evaluate statically known constraints at compile time and to remove them
> from all sorts of dopes. So instead of hiding the skeleton in the cupboard
> I would openly present it to the public! (:-))

I don't know how a language standard can *force* things like that.

> I see. But that will require definition of which parts of which statements
> may act as scopes: loop, if alternative, case choice, exception choice
> (already so in the "X : others"-kludge), select choice etc. How many pages
> of ARM? (:-))

A small fraction of one page.  ;-)
Any statement list should act as a scope.

> >>    T := (1, 2, 4, declare X := 9, others => declare Y := 10);
> >>       -- What would be this? How many Y's could be here?
> > 
> > No, I don't propose to allow mixing of decls and expressions!
> > Just decls and statements.  Decls don't return values.
> 
> Once you let them in, then: if I can put declarations among statements, why
> cannot I put statements among declarations?
                                ^^^^^^^^^^^^
You mean expressions?  You can't put statements (or declarations) among
expressions because the type is wrong -- a statement does not return a
value of the right type (it doesn't return a value at all, or in C
terminology, it returns "void".)

> >> So I see no advantage in this.
> > 
> > Convinced?  Partly convinced?  ;-)
> 
> I see the rationale and the problem, but the solution ...

Fair enough.

- Bob



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

* Re: advice on package design
  2005-03-18  9:38                         ` Martin Dowie
@ 2005-03-21 16:19                           ` Robert A Duff
  0 siblings, 0 replies; 28+ messages in thread
From: Robert A Duff @ 2005-03-21 16:19 UTC (permalink / raw)


"Martin Dowie" <martin.dowie@baesystems.com> writes:

> Robert A Duff wrote:
> >> I've never understood why this needs to be this way...
> >
> > Because I2 is visible in the handler.  But it's declaration is never
> > successfully elaborated, so it does not exist!
> >
> > procedure P is
> >     X: Integer := F(...); -- raises Some_Error
> >     Y: Integer := 123;
> > begin
> >     ...
> > exception
> >     when Some_Error =>
> >         Put(X); Put(Y); -- What are the values of X and Y here?
> > end P;
> 
> And we couldn't have come up with some form of words to get
> round that?!?!?!?! :-)

It's not a wording issue.  It just doesn't make any sense to allow code
to refer to variables that do not exist.

What's needed is a syntax that better reflects the semantics.
It was a mistake in Ada 83 to attach handlers to scope-y things.

- Bob



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

* Re: advice on package design
  2005-03-21 16:17                           ` Robert A Duff
@ 2005-03-21 18:16                             ` Dmitry A. Kazakov
  2005-03-21 20:35                               ` Robert A Duff
  0 siblings, 1 reply; 28+ messages in thread
From: Dmitry A. Kazakov @ 2005-03-21 18:16 UTC (permalink / raw)


On 21 Mar 2005 11:17:28 -0500, Robert A Duff wrote:

> "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> writes:
> 
>> On 17 Mar 2005 16:26:22 -0500, Robert A Duff wrote:
>> 
>>> "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> writes:
>>> 
>>>> I like the separation very much.
>>> 
>>> You're not alone.
>>> 
>>> Part of my reason is that declarations in Ada execute code at run time.
>>> In other languages (e.g. Pascal) there is a strict separation:
>>> declarations declare, statements execute, so it makes sense to have
>>> a syntactic line drawn between the two.  But in Ada, the decls can
>>> do real work, so the line drawn by the "begin" is misleading.
>> 
>> Yes, indirectly they do. For the following body this is the code that never
>> fail. Similarly, each "end;" is in fact also a code which again never fails
>> (for the body above). I can buy this as modularization (can I sell it so
>> too? (:-))
> 
> The problem is that whether something goes above or below the "begin"
> depends on all kinds of extraneous factors.  If I want to initialize a
> variable with a function, it goes above the line.  If I want to use
> a procedure call, or a loop, it goes below the line.  Etc.

The effects of the declaration of an initialized variable and of a
procedure call are fundamentally different. The first creates a new object
for following scope. Evaluation of that object is rather a side effect. All
things visible within the scope should be elaborated before "begin". What
if I rename "declare" to "require", i.e. consider the declaration block as
a precondition for following "begin". Will it look logical then?

require
   X : T := ...;
begin
   -- do something with X
end; -- ensure that there is no X anymore

The problem and difference with a procedure call is that it has no visible
effect. If it had, then that could be put in the declaration block.

>>> In some cases, the decls do *all* the work, especially when you're
>>> working with Strings, where the length usually depends on the 
>>> initial value.  Then the procedure body says "null;", meaning
>>> "this does nothing", which is a lie.
>> 
>> Yes. A similar case is:
>> 
>> if 0 = Some_Win_API (...) then
>>    null; -- Don't care about your silly return codes!
>> end if;
> 
> It doesn't seem similar to me.  Here, we are explicitly saying that the
> "then" part does nothing, and that's a good thing. 

But the only effect of Some_Win_API is its side effect. Using if-then-else
here is as misleading as null body of a procedure. It could even be written
as such a procedure!

procedure Some_Win_API_Wrapper (...) is
   Dummy : BOOL := Some_Win_API (...);
begin
   null;
end Some_Win_API_Wrapper;

> (Some other
> languages are more error prone in this regard.)  But a procedure that
> does a lot of work that happens to be above the "begin" still needs
> "null", which seems sort of silly.

I agree. But I think that the solution is a better balance between effects
and side effects.

I'd like to think of declarations as things done in parallel as opposed to
serialized statements. Unfortunately this is also untrue in Ada.
Serialization is extra coupling, I just feel that something must be wrong
with that (like that "and" vs. "and then" debate, no good arguments for
"and", but still...)

>> I also dislike unnecessary indentations, but what if exception handling is
>> needed? Then you have to create blocks anyway.
> 
> I would prefer a separate try/catch-like statement for exception
> handling.

In C++ that only increases nesting as compared to Ada.

> Exception handling seems totally unrelated to declaring
> things, so I don't see why they should be mixed in the same syntax.

It is rather mixed with scopes, which seems reasonable. Especially, if we
consider exceptions as a part of the contract, which IMO is the only right
way.

>>... and also for forcing the compiler to
>> evaluate statically known constraints at compile time and to remove them
>> from all sorts of dopes. So instead of hiding the skeleton in the cupboard
>> I would openly present it to the public! (:-))
> 
> I don't know how a language standard can *force* things like that.

But it forces evaluation of static constants. We have to allow user-defined
operations there. The type developer should be able to specify whether the
discriminant (constraint) is embedded in each value or not. Then there
should be a mechanism of constraint propagation like in:

type (Size : Positive) is array (...) of Object (Size);

>> I see. But that will require definition of which parts of which statements
>> may act as scopes: loop, if alternative, case choice, exception choice
>> (already so in the "X : others"-kludge), select choice etc. How many pages
>> of ARM? (:-))
> 
> A small fraction of one page.  ;-)
> Any statement list should act as a scope.

That will do the trick! (:-))

>>>>    T := (1, 2, 4, declare X := 9, others => declare Y := 10);
>>>>       -- What would be this? How many Y's could be here?
>>> 
>>> No, I don't propose to allow mixing of decls and expressions!
>>> Just decls and statements.  Decls don't return values.
>> 
>> Once you let them in, then: if I can put declarations among statements, why
>> cannot I put statements among declarations?
>                                 ^^^^^^^^^^^^
> You mean expressions?  You can't put statements (or declarations) among
> expressions because the type is wrong -- a statement does not return a
> value of the right type (it doesn't return a value at all, or in C
> terminology, it returns "void".)

Parameterless functions do something out of nothing (:-)). But in the first
place I meant declarations. Isn't it equivalent: if declarations and
statements can be mixed then it is commutative and there is no more any
distinction between them. So the following should be legal:

package Funny is
   for I in 1..100 loop
      ...
   end loop;
end Foo;

package body Foo is
   X : Integer := 10;
end Foo;

BTW, it seems that there is a parallel to if-elsif-elsif-else. What about
something in this spirit:

declare ... begin
   <statements>
then declare ... begin -- Better keywords needed
   <statements>
then declare ... begin
   <statements>
then declare ... begin
   <statements>
end;

It looks more structured than floating declarations. One could allow it in
all lists of statements.

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



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

* Re: advice on package design
  2005-03-21 18:16                             ` Dmitry A. Kazakov
@ 2005-03-21 20:35                               ` Robert A Duff
  2005-03-22 10:55                                 ` Dmitry A. Kazakov
  0 siblings, 1 reply; 28+ messages in thread
From: Robert A Duff @ 2005-03-21 20:35 UTC (permalink / raw)


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

> The effects of the declaration of an initialized variable and of a
> procedure call are fundamentally different. The first creates a new object
> for following scope. Evaluation of that object is rather a side effect. All
> things visible within the scope should be elaborated before "begin".

Well, I've considered a language design in which initialization (or
should it be called "creation") is allowed to happen after the
declaration.  In this language, initialization has a different syntax
than assignment (":=" for init, ":==" for assignment, or some such).

So you could do:

    procedure P is
        X: T'Class; -- perhaps some syntax saying "do not default init".

        if ... then
            X := ...;
        else
            X := ...;
        end if;
        ...

You can do that in Ada, but you have to introduce pointers,
and you don't always want pointer semantics.

I would have compile-time rules that ensure an object is initialized
(created?) before being used or assigned; the compiler would have to do
a bit of flow analysis to check these rules.

If you do *that*, then an 'out' parameter can either assign into the
actual, or initialize the actual.  Programmer's choice.

>... What
> if I rename "declare" to "require", i.e. consider the declaration block as
> a precondition for following "begin". Will it look logical then?
> 
> require
>    X : T := ...;
> begin
>    -- do something with X
> end; -- ensure that there is no X anymore

Sorry, I guess this is a matter of taste, and my taste says I don't like
the extra verbiage required just to declare X.

Note that I'm not against block statements.  I would allow:

    procedure P is
        begin
            X: ...;
            Y: ...;

            some statements
        end;
        begin
            -- X and Y not visible here.
            A: ...;
            B: ...;

            some statements
        end;
    end P;

or something like that.  But I wouldn't *require* the begin/end,
if you already have "then/end if" or something that delimits
the scope.

> The problem and difference with a procedure call is that it has no visible
> effect. If it had, then that could be put in the declaration block.
> 
> >>> In some cases, the decls do *all* the work, especially when you're
> >>> working with Strings, where the length usually depends on the 
> >>> initial value.  Then the procedure body says "null;", meaning
> >>> "this does nothing", which is a lie.
> >> 
> >> Yes. A similar case is:
> >> 
> >> if 0 = Some_Win_API (...) then
> >>    null; -- Don't care about your silly return codes!
> >> end if;
> > 
> > It doesn't seem similar to me.  Here, we are explicitly saying that the
> > "then" part does nothing, and that's a good thing. 
> 
> But the only effect of Some_Win_API is its side effect. Using if-then-else
> here is as misleading as null body of a procedure. It could even be written
> as such a procedure!

Yes, I suppose you're right about that.  I suppose the best way to write
that would be:

    Ignore_Result(Some_Win_API(...));

where Ignore_Result is a predefined procedure that takes a parameter of
type Anything'Class.

> procedure Some_Win_API_Wrapper (...) is
>    Dummy : BOOL := Some_Win_API (...);
> begin
>    null;
> end Some_Win_API_Wrapper;
> 
> > (Some other
> > languages are more error prone in this regard.)  But a procedure that
> > does a lot of work that happens to be above the "begin" still needs
> > "null", which seems sort of silly.
> 
> I agree. But I think that the solution is a better balance between effects
> and side effects.
> 
> I'd like to think of declarations as things done in parallel as opposed to
> serialized statements. Unfortunately this is also untrue in Ada.
> Serialization is extra coupling, I just feel that something must be wrong
> with that (like that "and" vs. "and then" debate, no good arguments for
> "and", but still...)

I agree, but I think the same is true of statements.  I've thought of
giving the programmer the choice: evaluate a sequence in parallel (and
the compiler checks that there are no bad interactions) or evaluate a
sequence in textual order.  Perhaps use "," to separate parallel things,
and ";" to separate sequential things.  So:

    X: Integer := 1,
    Y: Integer := 2;
    Z: Integer := X + Y;

meaning first create and initialize X and Y in parallel, and then create
and init Z.  I agree with your "extra coupling" point, but I think it
applies equally to decls and statements.  And I think the programmer
should have the choice of specifying sequential order (in both cases).

> >> I also dislike unnecessary indentations, but what if exception handling is
> >> needed? Then you have to create blocks anyway.
> > 
> > I would prefer a separate try/catch-like statement for exception
> > handling.
> 
> In C++ that only increases nesting as compared to Ada.

That's OK -- the extra nesting implies some meaning.

> > Exception handling seems totally unrelated to declaring
> > things, so I don't see why they should be mixed in the same syntax.
> 
> It is rather mixed with scopes, which seems reasonable. Especially, if we
> consider exceptions as a part of the contract, which IMO is the only right
> way.

I agree about the contract.  Java does this, but it gets some details
wrong.  I want a way to specify defaults.  For example, in a real-time
embedded program, I might want to explicitly mark every procedure that
can raise Storage_Error, but in a compiler, that's a waste, since
pretty-much anything can raise S_E.  Also, when passing procedures as
parameters, as in iterators, I want a way to say "this thing propagates
whatever the passed procedure propagates".

> >>... and also for forcing the compiler to
> >> evaluate statically known constraints at compile time and to remove them
> >> from all sorts of dopes. So instead of hiding the skeleton in the cupboard
> >> I would openly present it to the public! (:-))
> > 
> > I don't know how a language standard can *force* things like that.
> 
> But it forces evaluation of static constants.

It pretends to.  I claim you can't write an ACATS test that determines
whether an Ada compiler evaluates static constants at compile time.

But that's OK -- compiler writers are sometimes stupid or lazy, but we
can trust them to not deliberately sabotage their products!

>...We have to allow user-defined
> operations there. The type developer should be able to specify whether the
> discriminant (constraint) is embedded in each value or not. Then there
> should be a mechanism of constraint propagation like in:
> 
> type (Size : Positive) is array (...) of Object (Size);

Yes, I have similar notions about how compilers should deal with
discrims (stored with objects vs. not stored with objects).
Pragmas seem appropriate here, since it's purely an efficiency choice.
It's like pragma Inline -- sometimes you want it, sometimes you don't
(both for efficiency), but there's no ACATS test that can detect
whether the compiler is obeying your pragmas Inline.

Note that Ada 9X proposed to allow discriminated arrays,
but unfortunately that was rejected.

> >> I see. But that will require definition of which parts of which statements
> >> may act as scopes: loop, if alternative, case choice, exception choice
> >> (already so in the "X : others"-kludge), select choice etc. How many pages
> >> of ARM? (:-))
> > 
> > A small fraction of one page.  ;-)
> > Any statement list should act as a scope.
> 
> That will do the trick! (:-))
> 
> >>>>    T := (1, 2, 4, declare X := 9, others => declare Y := 10);
> >>>>       -- What would be this? How many Y's could be here?
> >>> 
> >>> No, I don't propose to allow mixing of decls and expressions!
> >>> Just decls and statements.  Decls don't return values.
> >> 
> >> Once you let them in, then: if I can put declarations among statements, why
> >> cannot I put statements among declarations?
> >                                 ^^^^^^^^^^^^
> > You mean expressions?  You can't put statements (or declarations) among
> > expressions because the type is wrong -- a statement does not return a
> > value of the right type (it doesn't return a value at all, or in C
> > terminology, it returns "void".)
> 
> Parameterless functions do something out of nothing (:-)). But in the first
> place I meant declarations. Isn't it equivalent: if declarations and
> statements can be mixed then it is commutative and there is no more any
> distinction between them. So the following should be legal:
> 
> package Funny is
>    for I in 1..100 loop
>       ...
>    end loop;
> end Foo;
> 
> package body Foo is
>    X : Integer := 10;
> end Foo;
> 
> BTW, it seems that there is a parallel to if-elsif-elsif-else. What about
> something in this spirit:
> 
> declare ... begin
>    <statements>
> then declare ... begin -- Better keywords needed
>    <statements>
> then declare ... begin
>    <statements>
> then declare ... begin
>    <statements>
> end;
> 
> It looks more structured than floating declarations. One could allow it in
> all lists of statements.

How about if I just promise to leave a blank line between groups of
decls and groups of statements?  ;-)

    begin
        Obj1: ...;
        Obj2: ...;

        Grind_Upon(Obj1, Obj2);

        Obj3: ...;

        Obj3 := Obj1 + Obj2;
        Obj3 := Obj3 + 1;
    end;

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

- Bob

P.S. I suppose the folks who want to talk about Ada on comp.lang.ada (as
opposed to some mythical language that is like Ada, only different) are
rather bored by this discussion.  But I think language design is fun!



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

* Re: advice on package design
  2005-03-21 20:35                               ` Robert A Duff
@ 2005-03-22 10:55                                 ` Dmitry A. Kazakov
  0 siblings, 0 replies; 28+ messages in thread
From: Dmitry A. Kazakov @ 2005-03-22 10:55 UTC (permalink / raw)


On 21 Mar 2005 15:35:36 -0500, Robert A Duff wrote:

> "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> writes:
> 
>> The effects of the declaration of an initialized variable and of a
>> procedure call are fundamentally different. The first creates a new object
>> for following scope. Evaluation of that object is rather a side effect. All
>> things visible within the scope should be elaborated before "begin".
> 
> Well, I've considered a language design in which initialization (or
> should it be called "creation") is allowed to happen after the
> declaration.  In this language, initialization has a different syntax
> than assignment (":=" for init, ":==" for assignment, or some such).
> 
> So you could do:
> 
>     procedure P is
>         X: T'Class; -- perhaps some syntax saying "do not default init".
> 
>         if ... then
>             X := ...;
>         else
>             X := ...;
>         end if;
>         ...

What about singleton functions?

procedure P is
   X : T'Class is
      begin
         if ... then
            return ...;
         else
            return ...;
         end if;
      end X;

equivalent to:

   function <anonymous> return T'Class is
   begin
      if ... then
         return ...;
      else
         return ...;
      end if;
   end <anonymous>;
   X : T'Class := <anonymous>;

> You can do that in Ada, but you have to introduce pointers,
> and you don't always want pointer semantics.
> 
> I would have compile-time rules that ensure an object is initialized
> (created?) before being used or assigned; the compiler would have to do
> a bit of flow analysis to check these rules.
> 
> If you do *that*, then an 'out' parameter can either assign into the
> actual, or initialize the actual.  Programmer's choice.

Hmm, lazy parameters, could they do the trick? You pass an out parameter as
lazy. The compiler skips any construction until first use in the body. So
construction vs. assignment would be controlled on the caller's side.

>>... What
>> if I rename "declare" to "require", i.e. consider the declaration block as
>> a precondition for following "begin". Will it look logical then?
>> 
>> require
>>    X : T := ...;
>> begin
>>    -- do something with X
>> end; -- ensure that there is no X anymore
> 
> Sorry, I guess this is a matter of taste, and my taste says I don't like
> the extra verbiage required just to declare X.
> 
> Note that I'm not against block statements.  I would allow:
> 
>     procedure P is
>         begin
>             X: ...;
>             Y: ...;
> 
>             some statements
>         end;
>         begin
>             -- X and Y not visible here.
>             A: ...;
>             B: ...;
> 
>             some statements
>         end;
>     end P;
> 
> or something like that.

In two steps away from curly brackets!... (:-))

>>> (Some other
>>> languages are more error prone in this regard.)  But a procedure that
>>> does a lot of work that happens to be above the "begin" still needs
>>> "null", which seems sort of silly.
>> 
>> I agree. But I think that the solution is a better balance between effects
>> and side effects.
>> 
>> I'd like to think of declarations as things done in parallel as opposed to
>> serialized statements. Unfortunately this is also untrue in Ada.
>> Serialization is extra coupling, I just feel that something must be wrong
>> with that (like that "and" vs. "and then" debate, no good arguments for
>> "and", but still...)
> 
> I agree, but I think the same is true of statements.  I've thought of
> giving the programmer the choice: evaluate a sequence in parallel (and
> the compiler checks that there are no bad interactions) or evaluate a
> sequence in textual order.  Perhaps use "," to separate parallel things,
> and ";" to separate sequential things.  So:
> 
>     X: Integer := 1,
>     Y: Integer := 2;
>     Z: Integer := X + Y;
> 
> meaning first create and initialize X and Y in parallel, and then create
> and init Z.

I believe there was something like that in Occam. However if there are two
"gluing operators" ',' and ';' then there must be also order brackets for
them.

> I agree about the contract.  Java does this, but it gets some details
> wrong.  I want a way to specify defaults.  For example, in a real-time
> embedded program, I might want to explicitly mark every procedure that
> can raise Storage_Error, but in a compiler, that's a waste, since
> pretty-much anything can raise S_E.  Also, when passing procedures as
> parameters, as in iterators, I want a way to say "this thing propagates
> whatever the passed procedure propagates".

Yes. Some kind of contract algebra is definitely needed. The difficult
issue is to get it right for interfaces, overriding, generics.

>>...We have to allow user-defined
>> operations there. The type developer should be able to specify whether the
>> discriminant (constraint) is embedded in each value or not. Then there
>> should be a mechanism of constraint propagation like in:
>> 
>> type (Size : Positive) is array (...) of Object (Size);
> 
> Yes, I have similar notions about how compilers should deal with
> discrims (stored with objects vs. not stored with objects).
> Pragmas seem appropriate here, since it's purely an efficiency choice.
> It's like pragma Inline -- sometimes you want it, sometimes you don't
> (both for efficiency), but there's no ACATS test that can detect
> whether the compiler is obeying your pragmas Inline.

Yes, if it does not influence the semantics (pragma's def), then there is
no way to detect if the compiler obey it... (:-))

However, for differently constrained subtypes there is a backdoor: T'Size =
S'Size. Actually T'Size and X'Size violate a lot of things and should
probably be disallowed. On the other hand it could be interesting to allow
something like:

type T is ...;
for T'Class'Size use <some-upper-limit>;
type Array_Of_T is array (...) of T'Class; -- OK because definite
type S is new T with ...; -- Compiler checks the size constraint

Though size in bits is too low-level and potentially non-portable. There
should be a better way...

>> BTW, it seems that there is a parallel to if-elsif-elsif-else. What about
>> something in this spirit:
>> 
>> declare ... begin
>>    <statements>
>> then declare ... begin -- Better keywords needed
>>    <statements>
>> then declare ... begin
>>    <statements>
>> then declare ... begin
>>    <statements>
>> end;
>> 
>> It looks more structured than floating declarations. One could allow it in
>> all lists of statements.
> 
> How about if I just promise to leave a blank line between groups of
> decls and groups of statements?  ;-)

I believe you (:-)), but what would you do with:

begin
   Obj1: ...;
   Obj2: ...;
 
   Grind_Upon(Obj1, Obj2);

   if Halt (x) then
      goto A;
   end if;

<<B>>
   Obj3: ...;

<<A>>
   Obj3 := Obj1 + Obj2;
   Obj3 := Obj3 + 1;

   if Halt (y) then
      goto B;
   end if;

end;

The above would require multiple construction/destruction of Obj3, stack
wind-ups all at run-time etc.

> P.S. I suppose the folks who want to talk about Ada on comp.lang.ada (as
> opposed to some mythical language that is like Ada, only different) are
> rather bored by this discussion.

They are busy writing AIs for Ada 2010! (:-))

> But I think language design is fun!

Yes

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



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

end of thread, other threads:[~2005-03-22 10:55 UTC | newest]

Thread overview: 28+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2005-03-07 16:23 advice on package design spambox
2005-03-07 21:08 ` Dmitry A. Kazakov
2005-03-08 12:48   ` spambox
2005-03-08 17:18     ` Dmitry A. Kazakov
2005-03-12 19:57   ` Robert A Duff
2005-03-12 20:45     ` Dmitry A. Kazakov
2005-03-12 21:59       ` Robert A Duff
2005-03-13  9:23         ` Dmitry A. Kazakov
2005-03-16 20:41           ` Robert A Duff
2005-03-17 10:22             ` Dmitry A. Kazakov
2005-03-17 14:04               ` Robert A Duff
2005-03-17 15:59                 ` Dmitry A. Kazakov
2005-03-17 19:10                   ` Robert A Duff
2005-03-17 19:47                     ` Martin Dowie
2005-03-17 20:55                       ` Robert A Duff
2005-03-17 21:14                         ` Marius Amado Alves
2005-03-18  9:31                           ` Martin Dowie
2005-03-18  9:38                         ` Martin Dowie
2005-03-21 16:19                           ` Robert A Duff
2005-03-17 20:48                     ` Dmitry A. Kazakov
2005-03-17 21:26                       ` Robert A Duff
2005-03-18  3:06                         ` Jared
2005-03-18 10:00                         ` Dmitry A. Kazakov
2005-03-21 16:17                           ` Robert A Duff
2005-03-21 18:16                             ` Dmitry A. Kazakov
2005-03-21 20:35                               ` Robert A Duff
2005-03-22 10:55                                 ` Dmitry A. Kazakov
2005-03-17 23:23                 ` Randy Brukardt

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