comp.lang.ada
 help / color / mirror / Atom feed
* some questions re. Ada/GNAT from a C++/GCC user
@ 1996-03-27  0:00 Bill Newman
  1996-03-27  0:00 ` Robert Dewar
                   ` (5 more replies)
  0 siblings, 6 replies; 80+ messages in thread
From: Bill Newman @ 1996-03-27  0:00 UTC (permalink / raw)


I have been skimming _Ada as a Second Language_ (second edition) and I
have looked at the GNAT documentation.  I am impressed: Ada and GNAT
look like they should do a pretty good job of meeting their design
criteria.  However, before I start planning my next project in Ada :-)
there are a few things I'd like to know..  (I hope against hope that
the tone of the answers will resemble the cross-language comparisons
in _Ada as a Second Language_ more closely than it resembles the
recent C++/Ada flamewar.)

The Ada Programming FAQ pooh-poohs STL, but I like it.  (Yes, I know I
could write my own versions of what I need, but a bunch of Ada
programmers shouldn't need to be told that that's not the ideal
solution.)  This FAQ also says the Booch components library is coming
-- when?

Does GNAT completely implement generics as defined in the standard?
(I ask because I have heard that no compiler, G++ otherwise, has yet
implemented C++ templates completely, and the G++ implementation
caused me lots of hassles before 2.7.x, and still causes some hassles
now.)

I *assume* that GNAT supports exceptions completely since they're an integral
part of the language and I didn't see any disclaimers, but since AFAIK G++
doesn't do them very well, I'd like to double-check: how well does
GNAT do exceptions?

I didn't notice anything about garbage collection in the GNAT docs, so
I assume it doesn't support it.  Will GNAT support GC in the
foreseeable future?

How well does GDB work with GNAT output?  Is it possible to get GDB to
interactively call arbitrary procedures/functions from GNAT-generated
code?  I have been very frustrated by the way that I can't get GDB to
call operator<<(ostream&, const Some_Class&) interactively, which
makes it very painful to inspect the state of a program produced by
G++.  (I do *not* appreciate having to inspect my data raw field by
raw field for every object in a complicated graph.)  Would I have the
same problem when inspecting the state of programs produced by GNAT?

Is there any way in Ada to iterate abstractly over the contents of a 
container, i.e. without writing each loop in a way which depends
strongly on the implementation of the container?  I know I 
could define a container class Foo_Basket_Type which would 
let me do something like
    for I = 1 .. Size(Foo_Basket) loop -- class implemented as array
       Sum := Sum + Bletchery(Element(Foo_Basket, I));
    end loop;
or 
    I : Foo_Basket_Iterator_Type := Head(Foo_Basket);
    ..
    while not Is_Done(I) loop -- class implemented as list
       Sum := Sum + Bletchery(dereference I);
       I := Next(I);
    end loop;
but what I'd really like to is something like 
    for each I in Foo_Basket loop -- don't much care how class is implemented
       Sum := Sum + Bletchery(dereference I);
    end loop;
I understand that in languages like Sather, I could do this without
macros.  In G++ I can come pretty close to this with CPP macros:
    FOR_EACH(i, foo_basket)
      sum += bletchery(*i);
In standard C++ (without the G++ `typeof' operator) I'd write something like
    FOR_EACH(i, Foo_Basket::Iterator, foo_basket)
      sum += bletchery(*i);
I use this idiom a lot -- about once per 40 lines of code in my
current project.  I much prefer it to the alternative of making my
code depend on the implementation of the container.  Is there any way of
doing something like this in Ada?  (I imagine there is, since the
alternatives look unnecessarily difficult to maintain.)
    
Due in part to my problems with GDB mentioned above, my C++ programs
tend to contain a lot of calls to macros MUTTER1, MUTTER2,
etc. defined either as no-op (for low levels of verbosity) or as
   #define MUTTER1(x) do { cerr << mutter_prefix << x << '\n'; } while (false)
so that I can write things like 
   MUTTER1("done with sampling, w = " << w << ", table = " << table);
concisely.  It's my impression that Ada's I/O facilities make it hard
to do trivial things like this concisely.  Is this wrong?  I don't really
want to have to write 
   if (Global_Verbosity >= 1) then
     Write_Mutter_Indent(Global_Mutter);
     Write(Global_Mutter, "done with sampling, W = ");
     Write(Global_Mutter, W);
     Write(Global_Mutter, ", table = ");
     Write(Global_Mutter, Table);
     Write_Line(Global_Error); -- silly typo encouraged by excess verbosity
   end if;
for a simple statement like the one above.

When I make two different instantiations of a generic package with the
same arguments, I understand the compiler treats them formally as two
different packages, which is OK with me.  However, I'd appreciate
knowing the compiler wouldn't actually output two redundant copies of
the corresponding (identical?) machine code, but instead share the
code.  I saw somewhere that the compiler is given considerable freedom
to share one instantiation between several arguments if it thinks it's
appropriate, which is also OK with me.  However, I haven't seen any
guarantee that the compiler won't output redundant copies for
instantiations with identical arguments.  Is there such a guarantee?

Can someone give me an idea what kind of compilation speed I could
expect from GNAT on a 486DX2/66 with 20 Mb of RAM, for projects of
1,000-50,000 lines?  (I'm interested both in the time required for
complete recompilation and in the time required for recompilation
after a trivial local change.)

My examples of `for each' and `mutter' above share a common feature:
I'd like to abstract away a common pattern so that I only need to type
each argument once, and I haven't figured out how to do it in Ada.  I
don't object to the rest of the verbosity I have seen in Ada: I can
see that it might make programs more readable, and since it's checked
by the compiler it shouldn't make programs significantly harder to
maintain.  However, the kind of verbosity required to hand-code
patterns like `for each' and `mutter' does not seem to make code more
readable, and since it is not checked by the compiler I'm afraid it
might cause maintenance problems by making it possible to add bugs by
changing some but not all occurrences of the redundant terms.  Are
there ways (e.g.  in the cases described above, or other cases that I
haven't thought of yet) in which Ada will force me to do this kind of
thing where C++ wouldn't?  If so, is this a bad thing (as I suspect),
or is it a positive feature in some sense that I haven't figured out,
or is it just the price we pay for the benefit of being guaranteed
that no one has abused macros in the program?

I also have a few idle questions about the design and application of
Ada 95, more-or-less unrelated to the question of whether I'll decide
to use it:

Why doesn't Ada 95 allow declarations to be interspersed with ordinary
statements as C++ does?  (Or does it?  _Ada as a Second Language_ is a
big book!)  It seems to me that the C++ approach is a small but
definite win.  Does it interact very badly somehow with all those
guarantees on elaboration order?

Someone remarked -- in this newsgroup recently IIRC -- that macros
were explicitly disallowed in the design goals for Ada (Ada 83?),
since they make programs hard to understand.  I don't remember the
exact wording, but as I remember the key reason was that you would
never know what was a macro and what wasn't without reading the entire
program hunting for macro definitions.  (I searched for `macro' in the
Rationale without success, so I'm just going on speculation and dim
memory here.)  It seems to me that that is a funny objection: most
macro processors these days don't require a characteristic pattern to
introduce macro expansions, but as far as I can tell there's no reason
that you couldn't restrict macro expansion to e.g. patterns preceded
by the keyword `macro'.  Granted, C's macro facilities are fairly
disgusting and inconsistent with the design goals of Ada, but it seems
to me a macro facility (or `generic syntax' facility?) more consistent
with Ada could be useful -- perhaps something closer to Scheme's
`hygienic macros' than to C's macro preprocessor.  Some restrictions
(e.g. only allowing macros to expand into expressions, sequences of
statements, or sequences of declarations, and not into arbitrary textual
strings) and perhaps importing from Lisp the idea that macros are
transformations on patterns of language tokens and expressions, rather
than transformations on patterns of characters) could do a pretty
decent job of discouraging people from writing truly screwy macros
without forbidding useful macros like `For_Each' or (to generalize
`MUTTER') `Write_Sequence' (sending a bunch of items to the same
stream, and using the language's overloading facility to sort out
which version of the Write procedure to call for each).  Would this
have been a good idea, except perhaps that the language is too
complicated already, or would this be a bad idea for some more
fundamental reason?

Finally, I found it intriguing when someone (somewhere in the endless
C++ vs. Ada thread) described a program which used exceptions to
convert runtime errors to `graceful degradation' so successfully that
the program (for fire control?!) continued operating more-or-less
correctly despite numerous bugs.  It seems to me that for the kinds of
programs that I typically work on, that this would require more than
exceptions (perhaps divine intervention?)  but I can imagine that it
would be possible for a program which was conceptually like a bunch of
closed-loop feedback systems, without a lot of state.  Therefore, it's
my guess that the program in question was such a program.  Is that a
sensible guess?  If not, i.e. if this kind of performance would be
possible in a program with lots of state, can someone give me a
pointer to the theory of this kind of thing?  (And are there any
examples of such fault-tolerant programs available on the net?)
(Examples of programs with lots of state include computer operating
systems like UNIX (or Emacs:-), or perhaps an air-traffic control
system which needs to base its output on data up to twenty hours old
regarding flight departure times, radar and weather reports, etc.  An
example of a system without much state would be a translation of a
system of analog radar-guided AA gun controllers directly into
software.)

  Bill Newman
  wnewman@netcom




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

* Re: some questions re. Ada/GNAT from a C++/GCC user
  1996-03-27  0:00 some questions re. Ada/GNAT from a C++/GCC user Bill Newman
@ 1996-03-27  0:00 ` Robert Dewar
  1996-03-28  0:00   ` Brian Rogoff
  1996-03-28  0:00   ` Norman H. Cohen
  1996-03-28  0:00 ` Scott Leschke
                   ` (4 subsequent siblings)
  5 siblings, 2 replies; 80+ messages in thread
From: Robert Dewar @ 1996-03-27  0:00 UTC (permalink / raw)


Bill asked:

The Ada Programming FAQ pooh-poohs STL, but I like it.  (Yes, I know I
could write my own versions of what I need, but a bunch of Ada
programmers shouldn't need to be told that that's not the ideal
solution.)  This FAQ also says the Booch components library is coming

  Those opinions do not represent the opinions of all in the Ada community
  by any means. The STL is a remarkable piece of work in my opinion. There
  is some very nice work going on at Rensaleer rewriting STL in Ada.

Does GNAT completely implement generics as defined in the standard?

  Yes

I *assume* that GNAT supports exceptions completely

  You assume correctly

I didn't notice anything about garbage collection in the GNAT docs, so
I assume it doesn't support it.  Will GNAT support GC in the
foreseeable future?

  We have no such plans. Several volunteers have mentioned projects to
  this effect. Perhaps you would like to help in this area!

How well does GDB work with GNAT output?

  Very well. We have used GDB for years to debug GNAT itself, and many
  of our customers have used it to debug large programs. There are
  areas where improvements are possible and planned, particularly
  in the tasking area.

  You assume correctly

I didn't notice anything about garbage collection in the GNAT docs, so
I assume it doesn't support it.  Will GNAT support GC in the
foreseeable future?

  We have no such plans. Several volunteers have mentioned projects to
  this effect. Perhaps you would like to help in this area!

How well does GDB work with GNAT output?

  Very well. We have used GDB for years to debug GNAT itself, and many
  of our customers have used it to debug large programs. There are
  areas where improvements are possible and planned, particularly
  in the tasking area.

Is it possible to get GDB to
interactively call arbitrary procedures/functions from GNAT-generated
code?

  Yes.

When I make two different instantiations of a generic package with the
same arguments, I understand the compiler treats them formally as two
different packages, which is OK with me.  However, I'd appreciate
knowing the compiler wouldn't actually output two redundant copies of
the corresponding (identical?) machine code, but instead share the
code.

  It is not easy in general to share code. GNAT always generates separate
  code which results in optimizing time at the expense of space.
  There are no plans to change this. (Note of course that plans can
  always be changed by customers with requirements and $$$$)

Can someone give me an idea what kind of compilation speed I could
expect from GNAT on a 486DX2/66 with 20 Mb of RAM, for projects of
1,000-50,000 lines?

  Why not just try it? the cost of a trial copy of GNAT is not expensive!
  Generally we see compilation speeds of several thousand lines a minute
  on such machines, but there are many variables.

Robert Dewar
Ada Core Technologies





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

* Re: some questions re. Ada/GNAT from a C++/GCC user
  1996-03-27  0:00 some questions re. Ada/GNAT from a C++/GCC user Bill Newman
  1996-03-27  0:00 ` Robert Dewar
  1996-03-28  0:00 ` Scott Leschke
@ 1996-03-28  0:00 ` Ted Dennison
  1996-03-29  0:00   ` Adam Beneschan
  1996-03-29  0:00 ` Robert A Duff
                   ` (2 subsequent siblings)
  5 siblings, 1 reply; 80+ messages in thread
From: Ted Dennison @ 1996-03-28  0:00 UTC (permalink / raw)


Bill Newman wrote:
> 
> Does GNAT completely implement generics as defined in the standard?
> (I ask because I have heard that no compiler, G++ otherwise, has yet
> implemented C++ templates completely, and the G++ implementation
> caused me lots of hassles before 2.7.x, and still causes some hassles
> now.)

The environment in the Ada community is such that the community won't
accept an Ada compiler that doesn't match the standard, except in some
extreme circumstances (such as embedded programming). If it is in the
Ada standard, then you can pretty much count on an Ada compiler 
implementing it as specified (barring bugs of course).

> When I make two different instantiations of a generic package with the
> same arguments, I understand the compiler treats them formally as two
> different packages, which is OK with me.  However, I'd appreciate
> knowing the compiler wouldn't actually output two redundant copies of
> the corresponding (identical?) machine code, but instead share the
> code.  I saw somewhere that the compiler is given considerable freedom
> to share one instantiation between several arguments if it thinks it's
> appropriate, which is also OK with me.  However, I haven't seen any
> guarantee that the compiler won't output redundant copies for
> instantiations with identical arguments.  Is there such a guarantee?

If you want a guarantee, put the code you want shared in a non-generic
package or procedure, and call it from your generic package or
procedure.

> Why doesn't Ada 95 allow declarations to be interspersed with ordinary
> statements as C++ does?  (Or does it?  _Ada as a Second Language_ is a
> big book!)  It seems to me that the C++ approach is a small but
> definite win.  Does it interact very badly somehow with all those
> guarantees on elaboration order?

Not really. If you want to declare variables later in the code just 
use a declare block:
   declare
      Var1 : My_Type;
      ...
   begin
      Var1 := My_Value;
      ...
   end;

This has the added benefit of limiting the scope of Var1 to the
begin..end
block of code.

(paraphrase: where are macros?)

I hate macros!!! Anyone who has has ever tried to compile emacs from
sources should agree with me. Anyone who has ever tried to figure out
"portable" C code should agree with me. Practically all of the
functionality of macros can be implemented in Ada in other ways. Ways
that are much easier to understand and maintain.

And lastly, if you really like them, implement your own pre-processor.
You wouldn't be the first.

> Finally, I found it intriguing when someone (somewhere in the endless
> C++ vs. Ada thread) described a program which used exceptions to
> convert runtime errors to `graceful degradation' so successfully that
> the program (for fire control?!) continued operating more-or-less

If you are mostly doing command processing (and no human lives are at
stake), this makes perfect sense. Just because there is some kind of 
problem (or exceptional condition) with one command doesn't mean the 
others can't work perfectly. In this case, why core dump? Just log the 
problem and continue.


-- 
T.E.D.          
                |  Work - mailto:dennison@escmail.orl.mmc.com  |
                |  Home - mailto:dennison@iag.net              |
                |  URL  - http://www.iag.net/~dennison         |




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

* Re: some questions re. Ada/GNAT from a C++/GCC user
  1996-03-27  0:00 ` Robert Dewar
  1996-03-28  0:00   ` Brian Rogoff
@ 1996-03-28  0:00   ` Norman H. Cohen
  1 sibling, 0 replies; 80+ messages in thread
From: Norman H. Cohen @ 1996-03-28  0:00 UTC (permalink / raw)


In article <dewar.827983273@schonberg>, dewar@cs.nyu.edu (Robert Dewar)
writes: 

|>                                                                     There
|>   is some very nice work going on at Rensaleer rewriting STL in Ada.

This is news to me, albeit very good news.  Can Robert or someone from
RPI provide us with some details?

--
Norman H. Cohen    ncohen@watson.ibm.com




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

* Re: some questions re. Ada/GNAT from a C++/GCC user
  1996-03-27  0:00 ` Robert Dewar
@ 1996-03-28  0:00   ` Brian Rogoff
  1996-03-29  0:00     ` John G. Volan
  1996-03-28  0:00   ` Norman H. Cohen
  1 sibling, 1 reply; 80+ messages in thread
From: Brian Rogoff @ 1996-03-28  0:00 UTC (permalink / raw)


In article <dewar.827983273@schonberg> dewar@cs.nyu.edu (Robert Dewar) writes:

   Bill asked:

   The Ada Programming FAQ pooh-poohs STL, but I like it.  (Yes, I know I
   could write my own versions of what I need, but a bunch of Ada
   programmers shouldn't need to be told that that's not the ideal
   solution.)  This FAQ also says the Booch components library is coming

     Those opinions do not represent the opinions of all in the Ada community
     by any means. The STL is a remarkable piece of work in my opinion. There
     is some very nice work going on at Rensaleer rewriting STL in Ada.

And while thousands of Ada 95 enthusiasts wait with bated breath for more 
information... :-)

The ftp site for STL is ftp://ftp.cs.rpi.edu/pub/stl/. In that directory, I found 
two files stl2ada.ps, and stl2ada-refimp-1.0.tar.gz. Apparently, a graduate student 
at RPI (akonstan.cs.rpi.edu) is porting the STL to Ada 95, and plans to have it 
done by May. The paper discusses some issues that arose in implementing the 
reference port. 

It would be nice to merge this work with dweller's Booch Components (shouldn't 
they be the "Dweller Components", it sure sounds better to me ;-) and thus 
provide a standard data structure library for Ada 95. 

BTW, that is "Rensselaer", and the natives of the town are "Troylets", in case 
you were wondering.

-- Brian






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

* Re: some questions re. Ada/GNAT from a C++/GCC user
  1996-03-27  0:00 some questions re. Ada/GNAT from a C++/GCC user Bill Newman
  1996-03-27  0:00 ` Robert Dewar
@ 1996-03-28  0:00 ` Scott Leschke
  1996-03-29  0:00   ` Robert A Duff
                     ` (2 more replies)
  1996-03-28  0:00 ` Ted Dennison
                   ` (3 subsequent siblings)
  5 siblings, 3 replies; 80+ messages in thread
From: Scott Leschke @ 1996-03-28  0:00 UTC (permalink / raw)


wnewman@netcom.com (Bill Newman) writes:

>When I make two different instantiations of a generic package with the
>same arguments, I understand the compiler treats them formally as two
>different packages, which is OK with me.  However, I'd appreciate
>knowing the compiler wouldn't actually output two redundant copies of
>the corresponding (identical?) machine code, but instead share the
>code.  I saw somewhere that the compiler is given considerable freedom
>to share one instantiation between several arguments if it thinks it's
>appropriate, which is also OK with me.  However, I haven't seen any
>guarantee that the compiler won't output redundant copies for
>instantiations with identical arguments.  Is there such a guarantee?

My first question would be, why do you want redundant instantiations.
Note that Ada separates instantiation of a generic from use of the
resulting package/subunit.  The general rule of thumb to avoid code-bloat
due to duplicate instantiations is to do the instantiation at the library
level and 'with' the resulting package/subunit.

>Why doesn't Ada 95 allow declarations to be interspersed with ordinary
>statements as C++ does?  (Or does it?  _Ada as a Second Language_ is a
>big book!)  It seems to me that the C++ approach is a small but
>definite win.  Does it interact very badly somehow with all those
>guarantees on elaboration order?

You can use a block statement.  This is different than C++ in the sense
that objects declared within the block are only in existence within the
block and are finalized at the end.  A block can also have its own
exception handlers.  The syntactic form is (from page 500 of the LRM):

block statement ::=
  [block_statement_identifier:]
    [declare
       declarative_part]
    begin
       handled_sequence_of_statements
    end [block_identifier];

Note that the block identifier and declaration section are optional.


For example:

declare
   Obj : Pkg.SomeType;
begin
   Pkg.Operation (Object => Obj);
   -- Other stuff
exception
   when Pkg.Some_Exception =>

      Do_Something;
end;
-- 
Scott Leschke.........................email: leschkes@cig.mot.com
Motorola, Inc............................ph: 847-632-2786
1501 W Shure Drive......................fax: 847-632-3145
Arlington Heights, IL   60004......mailstop: 1301




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

* Re: some questions re. Ada/GNAT from a C++/GCC user
@ 1996-03-28  0:00 Simon Johnston
  0 siblings, 0 replies; 80+ messages in thread
From: Simon Johnston @ 1996-03-28  0:00 UTC (permalink / raw)


> Bill asked:
>
> The Ada Programming FAQ pooh-poohs STL, but I like it.  (Yes, I know
> I could write my own versions of what I need, but a bunch of Ada
> programmers shouldn't need to be told that that's not the ideal
> solution.)  This FAQ also says the Booch components library is
> coming
>
>   Those opinions do not represent the opinions of all in the Ada
>   community by any means. The STL is a remarkable piece of work in
>   my opinion. There is some very nice work going on at Rensaleer
>   rewriting STL in Ada.

I also like the STL, I also have to use it in C++ development so an
Ada *equivalent* (not necessarily direct port) would be great. I read
a paper recently (can't lay my hand on it now) about implementing the
STL in Ada-95, which although favourable might lead non-Ada people to
believe that an implementation in Ada-95 would be hard, slow,
inefficient and unwieldy. I think such issues should be directly
challanged and lets have an STL-like library ASAP!!

> Does GNAT completely implement generics as defined in the standard?
>
>   Yes
>
> I *assume* that GNAT supports exceptions completely
>
>   You assume correctly
>
> I didn't notice anything about garbage collection in the GNAT docs,
> so I assume it doesn't support it.  Will GNAT support GC in the
> foreseeable future?
>
>   We have no such plans. Several volunteers have mentioned projects
>   to this effect. Perhaps you would like to help in this area!
>
> How well does GDB work with GNAT output?
>
>   Very well. We have used GDB for years to debug GNAT itself, and
>   many of our customers have used it to debug large programs. There
>   are areas where improvements are possible and planned,
>   particularly in the tasking area.
>
>   You assume correctly
>
> I didn't notice anything about garbage collection in the GNAT docs,
> so I assume it doesn't support it.  Will GNAT support GC in the
> foreseeable future?
>
>   We have no such plans. Several volunteers have mentioned projects
>   to this effect. Perhaps you would like to help in this area!
>
> How well does GDB work with GNAT output?
>
>   Very well. We have used GDB for years to debug GNAT itself, and
>   many of our customers have used it to debug large programs. There
>   are areas where improvements are possible and planned,
>   particularly in the tasking area.
>
> Is it possible to get GDB to
> interactively call arbitrary procedures/functions from
> GNAT-generated code?
>
>   Yes.
>
> When I make two different instantiations of a generic package with
> the same arguments, I understand the compiler treats them formally
> as two different packages, which is OK with me.  However, I'd
> appreciate knowing the compiler wouldn't actually output two
> redundant copies of the corresponding (identical?) machine code, but
> instead share the code.
>
>   It is not easy in general to share code. GNAT always generates
>   separate code which results in optimizing time at the expense of
>   space. There are no plans to change this. (Note of course that
>   plans can always be changed by customers with requirements and
>   $$$$)
>
> Can someone give me an idea what kind of compilation speed I could
> expect from GNAT on a 486DX2/66 with 20 Mb of RAM, for projects of
> 1,000-50,000 lines?
>
>   Why not just try it? the cost of a trial copy of GNAT is not
>   expensive! Generally we see compilation speeds of several thousand
>   lines a minute on such machines, but there are many variables.
>
> Robert Dewar
> Ada Core Technologies
>

with StandardDisclaimer; use StandardDisclaimer;
package Sig is
--,-------------------------------------------------------------------------.
--|Simon K. Johnston - Development Engineer (C++/Ada95) |ICL Retail Systems |
--|-----------------------------------------------------|3/4 Willoughby Road|
--|Unix Mail: skj@acm.org                               |Bracknell          |
--|Telephone: +44 (0)1344 476320 Fax: +44 (0)1344 476302|Berkshire          |
--|Internal : 7261 6320   OP Mail: S.K.Johnston@BRA0801 |RG12 8TJ           |
--|WWW URL  : http://www.acm.org/~skj/                  |United Kingdom     |
--`-------------------------------------------------------------------------'
end Sig;




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

* Re: some questions re. Ada/GNAT from a C++/GCC user
  1996-03-28  0:00 ` Scott Leschke
@ 1996-03-29  0:00   ` Robert A Duff
  1996-03-30  0:00     ` Richard Pitre
  1996-03-29  0:00   ` Robert I. Eachus
  1996-03-29  0:00   ` Bill Newman
  2 siblings, 1 reply; 80+ messages in thread
From: Robert A Duff @ 1996-03-29  0:00 UTC (permalink / raw)


In article <leschkes.828053356@ferret>,
Scott Leschke <leschkes@ferret.cig.mot.com> wrote:
>My first question would be, why do you want redundant instantiations.

Usually you don't.  But you might declare a type Apple_Count, and not
know whether anybody wants to do I/O on it.  Then one client does, and
instantiates Text_IO.Integer_IO on Apple_Count.  And another client
independently does the same.  One might like to share the code of the
two instantiations.

But, as Bill Newman pointed out, the more important thing is when you
have Apple_Count and Orange_Count, which are logically distinct, but
happen to share the same hardware representation.  That's a more
important case where you'd like shared generic code.

Shared generic code isn't easy to implement in general, but many Ada 83
compilers support it, at least for some kinds of generics.  The problem
is that you can't count on it in portable code, so you have to do it "by
hand".  That's not so awful, I suppose -- at least the ugly parts are
buried in one place.

>You can use a block statement.  This is different than C++ in the sense
>that objects declared within the block are only in existence within the
>block and are finalized at the end.

No, I think this is exactly the same in C++ and Ada.  See r.6.7 of The
C++ Programming Language, Second Edition, by Bjarne Stroustrup.  That
is, if you declare an object in C++ in a local sequence of statements,
it will be finalized at the end of that sequence.  The only difference
is that Ada requires some extra syntactic baggage.

>declare
>   Obj : Pkg.SomeType;
>begin
>   Pkg.Operation (Object => Obj);
>   -- Other stuff
>exception
>   when Pkg.Some_Exception =>
>
>      Do_Something;
>end;

The exception handler makes this look reasonable, but in the usual case,
where there's no need for any exception handling, and you just want to
declare Obj, the extra 3 lines of code ("declare", "begin", "end") are
just an annoyance.

- Bob




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

* Re: some questions re. Ada/GNAT from a C++/GCC user
  1996-03-28  0:00 ` Ted Dennison
@ 1996-03-29  0:00   ` Adam Beneschan
  0 siblings, 0 replies; 80+ messages in thread
From: Adam Beneschan @ 1996-03-29  0:00 UTC (permalink / raw)


Ted Dennison <dennison@escmail.orl.mmc.com> writes:
 
 >> When I make two different instantiations of a generic package with the
 >> same arguments, I understand the compiler treats them formally as two
 >> different packages, which is OK with me.  However, I'd appreciate
 >> knowing the compiler wouldn't actually output two redundant copies of
 >> the corresponding (identical?) machine code, but instead share the
 >> code.  I saw somewhere that the compiler is given considerable freedom
 >> to share one instantiation between several arguments if it thinks it's
 >> appropriate, which is also OK with me.  However, I haven't seen any
 >> guarantee that the compiler won't output redundant copies for
 >> instantiations with identical arguments.  Is there such a guarantee?
 >
 >If you want a guarantee, put the code you want shared in a non-generic
 >package or procedure, and call it from your generic package or
 >procedure.

I don't think this will always work, at least not cleanly.  Suppose
you have a generic whose only generic formal parameter is a private
type:
        
        generic
            type T is private;
        package GENERIC_PACKAGE is
            ... 
            procedure Do_Something (X : out T);
            procedure Do_Something_Else (X : in T);
            ...
        end GENERIC_PACKAGE;

About the only thing the generic package body can do with objects of
type T is to move them around.  So, ignoring for the moment strange
cases like unconstrained arrays, it should be easy for a compiler to
generate one copy of Do_Something and Do_Something_Else (and similarly
any procedure declared in the package body) for all instantiations of
GENERIC_PACKAGE.  Each procedure would take the size of T as an extra
parameter, and whenever the procedure needs to move objects of type T
around, it just does a raw byte copy, using the size parameter.  This
should run only slightly slower than if the compiler generated a
separate copy of Do_Something for every instantiation, and each copy
were able to treat the size as a known constant.

But suppose you have a compiler that always generates a separate copy
of all procedures for each instantiation.  How would you write a
non-generic shared procedure that you could call from the generic
package? 

    procedure Non_Generic_Do_Something (---well, what?)

What types would the parameters of this routine be?  The only
possibilities I can see would be to have this routine take a
SYSTEM.ADDRESS and a size parameter, or a byte array type, or some
other low-level type.  No matter how you do it, the code in
GENERIC_PACKAGE, in order to call Non_Generic_Do_Something, would have
to use UNCHECKED_CONVERSION or something like that (which doesn't fit
my definition of "cleanly").

Even if all you want is to make sure that only one copy of the code
is generated when the generic parameters are identical, how can you do
this by writing a shared non-generic routine?  If, in four different
places, you say:

    package GP1 is new GENERIC_PACKAGE (RECORD_TYPE_1);

    package GP2 is new GENERIC_PACKAGE (RECORD_TYPE_2);

    package GP3 is new GENERIC_PACKAGE (RECORD_TYPE_1);

    package GP4 is new GENERIC_PACKAGE (RECORD_TYPE_2);

How could you write the Non_Generic_Do_Something routine, and the
generic Do_Something procedure, so that just two versions of the
Do_Something code (one to handle RECORD_TYPE_1 and one to handle
RECORD_TYPE_2) are generated by the compiler?

                                -- Adam




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

* Re: some questions re. Ada/GNAT from a C++/GCC user
  1996-03-29  0:00 ` Robert A Duff
@ 1996-03-29  0:00   ` Brian Rogoff
  1996-04-01  0:00     ` Mark A Biggar
  1996-03-30  0:00   ` Iterators (was Re: some questions re. Ada/GNAT from a C++/GCC user) Robert I. Eachus
                     ` (3 subsequent siblings)
  4 siblings, 1 reply; 80+ messages in thread
From: Brian Rogoff @ 1996-03-29  0:00 UTC (permalink / raw)


In article <Dp1oAw.7Cz@world.std.com> bobduff@world.std.com (Robert A Duff) writes:
   > Is there any way in Ada to iterate abstractly over the contents of a 
   > container,...

   Several ways:

       - Write a generic procedure Iterate, which takes a formal
	 procedure representing the body of the loop -- that is,
	 the formal procedure says what to do for each element.

       - Write a procedure that takes an access-to-procedure as
	 a parameter.  In standard Ada, this only works if the
	 procedure being passed in is non-nested, which makes this
	 technique fairly useless.  In GNAT, you can "cheat", by
	 using 'Unrestricted_Access, but that's not standard Ada.

       - Write an Iterator class.  Define any particular loop
	 by deriving from that, and overriding the Do_One_Element
	 method, or whatever.  I think there's an example in the
	 Rationale.

       - For each data structure, define Start, Done, Current_Element,
	 and Get_Next operations.  Or some variation on this.

Jon Anthony, where are you? :-)
(FYI, Jon has a very nice approach to simulating Sather iters in Ada 95. I'll 
leave it for him to post.)

   None of these is as pretty as the Sather solution, but they achieve the
   main goal, which is to have the module that defines the data structure
   define how to loop, and the clients define what they do at each
   iteration.

The Sather iteration abstraction is great, and should be considered for 
inclusion in future variants of Ada. Maybe now that GNAT exists, someone 
will make an iter enhanced Ada like language (Sada? Sadie?).

   Macros can be used to achieve that, but in many cases, so can
   procedures, generics, Sather's iterators, etc.  IMHO, the more
   restricted features are generally better than macros.  Macros are more
   powerful, but also more difficult to understand.  TeX is a language that
   uses macros for just about everything, and I find TeX code to be totally
   incomprehensible, despite the fact that I've read the TeX Book 4 times.

I agree, although just about any feature can be used to create unreadable code. 
Even simple procedure abstraction (named procedures) could be used in an 
obfuscatory manner (call "read" "write", "plus" "minus", etc.), though macros 
give a lot of rope by which to hang yourself. There is supposedly some work by 
Cardelli somewhere on the web about a clean approach to macros (yes I know about 
Scheme; this is supposed to be different). OK, I found it (I love the Web :-), 
it is called  "Extensible Syntax with Lexical Scoping" and is available at 

	http://www.research.digital.com/SRC/personal/Luca_Cardelli/Papers.html

No I haven't read it; a friend told me about it. Looks interesting...

   > Why doesn't Ada 95 allow declarations to be interspersed with ordinary
   > statements as C++ does?  (Or does it?  _Ada as a Second Language_ is a
   > big book!)  It seems to me that the C++ approach is a small but
   > definite win.  Does it interact very badly somehow with all those
   > guarantees on elaboration order?

   To intersperse declarations, you have to use a block_statement, like
   this:

       for I in Some_String'Range loop -- I wish I could easily use an iterator here ;-)
	   declare
	       X: constant Character := Some_String(I);
	   begin
	       ...
	   end;
       end loop;

   To me, the "declare", "begin", and "end" are just useless verbosity.
   I prefer the C++ rule.

Yes, this seems needlessly verbose. Any Ada guru have a good reason why it has 
to be this way?

   > Someone remarked -- in this newsgroup recently IIRC -- that macros
   > were explicitly disallowed in the design goals for Ada (Ada 83?),
   > since they make programs hard to understand.  I don't remember the
   > exact wording, but as I remember the key reason was that you would
   > never know what was a macro and what wasn't without reading the entire
   > program hunting for macro definitions.  (I searched for `macro' in the
   > Rationale without success, so I'm just going on speculation and dim
   > memory here.)  It seems to me that that is a funny objection: most
   > macro processors these days don't require a characteristic pattern to
   > introduce macro expansions, but as far as I can tell there's no reason
   > that you couldn't restrict macro expansion to e.g. patterns preceded
   > by the keyword `macro'.

   I don't think this is the main reason people think macros are hard to
   understand.  As you say, it would be easy to design the syntax so that a
   macro invokation looks different from anything else.

   Macros are generally hard to understand for other reasons, I think.
   Part of the problem is the low-level character-based nature of macros.
   But as you point out below, that's not true of all known macro
   facilities.  Part of the problem is the lack of scoping rules -- if a
   macro expansion refers to a global variable X, it's referring to
   whatever X happens to be lying around at the point of the macro
   expansion.  Contrast that with Ada's generics, where the names are bound
   at the site of the generic itself.  Part of the problem is that you have
   to imagine what the macro expands to in order to understand what it
   does, and complex code trasformations are hard to imagine accurately.
   TeX macros have additional problems, like the fact that you can't tell
   at the call site how many arguments are being passed -- in fact, you
   can't in general tell until run time.

The Cardelli paper addresses the scoping issue (damn, now I'll have to read it).
Here is the abstract:

Extensible Syntax with Lexical Scoping 

A frequent dilemma in programming languages design is the choice between a language 
with a
rich set of notations and a small, simple core language. We address this dilemma by 
proposing
extensible grammars, a syntax-definition formalism for incremental language extensions 
ans
restrictions. The translation of program written in rich object languages into a small 
code language
is defined via syntax-directed patterns. In contrast to macro-expansion and 
program-rewriting
tools, our extensible grammars respect scoping rules. Therefore, we can introduce binding
constructs while avoiding problems with unwanted name clashes. We develop extensible
grammars and illustrate their use by extending the lambda calculus with let-bindings,
conditionals, and constructs from database programming languages, such as SQL query
expressions. We then give a formal description of the underlying rules for parsing, 
transformation,
and substitution. Finally, we sketch how these rules are exploited in an implementation
 of ageneric, extensible parser package. 

   - Bob

   P.S. I hope you enjoy Ada.  Why not get a copy of GNAT, and try it out?

Seconded!

-- Brian




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

* Re: some questions re. Ada/GNAT from a C++/GCC user
  1996-03-28  0:00   ` Brian Rogoff
@ 1996-03-29  0:00     ` John G. Volan
  1996-03-30  0:00       ` Robert A Duff
                         ` (2 more replies)
  0 siblings, 3 replies; 80+ messages in thread
From: John G. Volan @ 1996-03-29  0:00 UTC (permalink / raw)


In article <315AC5E7.3A77@escmail.orl.mmc.com>
Ted Dennison, dennison@escmail.orl.mmc.com writes:

>If you want to declare variables later in the code just 
>use a declare block:
>   declare
>      Var1 : My_Type;
>      ...
>   begin
>      Var1 := My_Value;
>      ...
>   end;
>
>This has the added benefit of limiting the scope of Var1 to the
>begin..end  block of code.

There is another benefit, but it isn't obvious unless you take into
account the concurrency aspects of Ada:

A C programmer would complain about having to introduce an extra
begin/end block in order to introduce variables.  Isn't the scope
of a variable well-defined in C, even without this extra baggage?
It just extends from the declaration to the end of whatever block
you're already in.

An Ada programmer would counter that, in Ada, some variables might be
instances of _task_ types.  By definition, a task object gets created
when its declaration is elaborated, but it does not get _activated_
(i.e., it doesn't start executing its statements) until you hit the
"begin" of the enclosing block.  This guarantees that everything in the
enclosing declarative region is elaborated before any a task starts up.
In fact, _all_ the tasks in a declarative region are activated by the
"begin", simultaneously.  (Or virtually simultaneously. The point is,
Ada does not prescribe any particular order of activation, and does not
preclude truly simultaneous activation if the underlying run time
environment can support it.)  In effect, if a declarative region
contains tasks, then there is a synchronization point at the "begin"
which the Ada programmer can rely on.

C, of course, has no concurrency abstractions built in (nor does C++,
last time I checked), so, gee, I guess this isn't an issue in C. :-)

------------------------------------------------------------------------
Internet.Usenet.Put_Signature
( Name => "John G. Volan", E_Mail => "John_Volan@dayton.saic.com",
  Favorite_Slogan => "Ada95: The *FIRST* International-Standard OOPL",
  Humorous_Disclaimer => "These opinions are undefined by SAIC, so" &
    "any use would be erroneous ... or is that a bounded error now?" );
------------------------------------------------------------------------




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

* Re: some questions re. Ada/GNAT from a C++/GCC user
  1996-03-28  0:00 ` Scott Leschke
  1996-03-29  0:00   ` Robert A Duff
@ 1996-03-29  0:00   ` Robert I. Eachus
  1996-03-29  0:00   ` Bill Newman
  2 siblings, 0 replies; 80+ messages in thread
From: Robert I. Eachus @ 1996-03-29  0:00 UTC (permalink / raw)



   wnewman@netcom.com (Bill Newman) writes:

   >When I make two different instantiations of a generic package with the
   >same arguments, I understand the compiler treats them formally as two
   >different packages, which is OK with me.  However, I'd appreciate
   >knowing the compiler wouldn't actually output two redundant copies of
   >the corresponding (identical?) machine code, but instead share the
   >code.  I saw somewhere that the compiler is given considerable freedom
   >to share one instantiation between several arguments if it thinks it's
   >appropriate, which is also OK with me.  However, I haven't seen any
   >guarantee that the compiler won't output redundant copies for
   >instantiations with identical arguments.  Is there such a guarantee?

   Uh, repeat after me... Generic instantiation happens at run-time.

   Compilers can, and in some cases must generate code for generic
units.  But since in many cases, the number of copies of that generic
needed cannot be determined--even during execution--the compiler has
to be able to share generic code, even if some of the generic actual
parameters are different.

   This is the reason why this "fallacy" needs to be rooted out of
programmers thinking.  Generics in Ada are very powerful, but only if
you understand that the generic parameters are evaluated when the
generic instantiation is elaborated.  The same generic can be
elaborated thousands or millions of times during a single program
execution.

   Take for example, the generic packages in Text_IO.  It might seem
that since each of these packages has only a formal type parameter,
that every instance created from a single generic instance declaration
is the same.  Not so!  See the example program below.  The SUBtype
matching a generic formal type parameter can be one that is only
determined at run-time, in this case by reading in the bounds from the
terminal.  I put it in a loop so that you can see that each iteration
of the loop there is a new package Workday_IO created, and these
different packages accept different inputs.

-----------------------------------------------------------------------
with Text_IO;
with Calendar;
procedure Test_Generics is
  type Day is (Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday);
  Start,Stop: Day;
  package Day_IO is new Text_IO.Enumeration_IO(Day);
begin
  loop
    Text_IO.New_Line;
    Text_IO.Put_Line(" Enter first day of work week: ");
    Day_IO.Get(Start);
    Text_IO.Put_Line(" Enter last day of work week: ");
    Day_IO.Get(Stop);
    if Start > Stop 
    then Text_IO.Put_Line(" Invalid range. Try again.");
    else
      declare
        subtype Workday is Day range Start..Stop;
        package Workday_IO is new Text_IO.Enumeration_IO(Workday);
        Worked: Workday;
      begin
        Text_IO.Put_Line(" Enter a day you work: ");
        Workday_IO.Get(Worked);
        Text_IO.Put_Line(" You worked " & Day'IMAGE(Worked) & '.');
      exception
        when Text_IO.Data_Error => 
          Text_IO.Put_Line(" Invalid day.  Try again.");
        when others =>
          Text_IO.Put_Line(" All done."); 
          return;
      end;
    end if;
  end loop;
end Test_Generics;
-----------------------------------------------------------------------------
spectre% a.ld test_generics -o TG
spectre% TG

 Enter first day of work week: 
Monday
 Enter last day of work week: 
Friday
 Enter a day you work: 
Tuesday
 You worked TUESDAY.

 Enter first day of work week: 
Wednesday
 Enter last day of work week: 
Saturday
 Enter a day you work: 
Tuesday
 Invalid day.  Try again.

 Enter first day of work week: 
Sunday
 Enter last day of work week: 
Wednesday
 Enter a day you work: 

\x03
spectre%
--

					Robert I. Eachus

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




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

* Re: some questions re. Ada/GNAT from a C++/GCC user
  1996-03-28  0:00 ` Scott Leschke
  1996-03-29  0:00   ` Robert A Duff
  1996-03-29  0:00   ` Robert I. Eachus
@ 1996-03-29  0:00   ` Bill Newman
  2 siblings, 0 replies; 80+ messages in thread
From: Bill Newman @ 1996-03-29  0:00 UTC (permalink / raw)


Scott Leschke (leschkes@ferret.cig.mot.com) wrote:
: wnewman@netcom.com (Bill Newman) writes:

: >When I make two different instantiations of a generic package with the
: >same arguments, I understand the compiler treats them formally as two
: >different packages, which is OK with me.  However, I'd appreciate
: >knowing the compiler wouldn't actually output two redundant copies of
: >the corresponding (identical?) machine code, but instead share the
: >code.  I saw somewhere that the compiler is given considerable freedom
: >to share one instantiation between several arguments if it thinks it's
: >appropriate, which is also OK with me.  However, I haven't seen any
: >guarantee that the compiler won't output redundant copies for
: >instantiations with identical arguments.  Is there such a guarantee?

: My first question would be, why do you want redundant instantiations.
: Note that Ada separates instantiation of a generic from use of the
: resulting package/subunit.  The general rule of thumb to avoid code-bloat
: due to duplicate instantiations is to do the instantiation at the library
: level and 'with' the resulting package/subunit.

I wondered about this because Ada encourages the creation of type
distinctions which don't reflect differences in underlying machine
representation.  In seems as though (mixing C++ template notation with
Ada's distinct types represented by machine floating point numbers)
Dynamically_Allocated_Stack_Of<Apple_Juice_Volume> and
Dynamically_Allocated_Stack_Of<Orange_Juice_Volume> could share
exactly the same underlying code.  It would be nice if the compiler
would recognize this without me having to figure out some way to
implement the templates/generics to make it explicit.  Even in C++
(without the option of conveniently making distinct Apples and Oranges
types out of e.g. float) this comes up frequently when you use
template containers of pointers to things: to the machine, a pointer
is a pointer, so the object code tends to be the same regardless of
the pointer type.  Sometimes in C++ I use a shared implementation for
such templates (using void* pointers, and the equivalent of Ada
Unchecked_Type_Conversion (?)) but even though the final result can be
type-safe if done correctly, it's tedious to make sure that it is, and
I'd rather not have to do it in Ada, especially since Ada strongly
discourages this sort of thing.  If the Ada type system multiplies the
number of cases where this would be useful, and if Ada discourages me
from doing it for myself, it would be nice if the Ada compiler would
do it for me.

  Bill Newman
  wnewman@netcom.com





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

* Re: some questions re. Ada/GNAT from a C++/GCC user
  1996-03-27  0:00 some questions re. Ada/GNAT from a C++/GCC user Bill Newman
                   ` (2 preceding siblings ...)
  1996-03-28  0:00 ` Ted Dennison
@ 1996-03-29  0:00 ` Robert A Duff
  1996-03-29  0:00   ` Brian Rogoff
                     ` (4 more replies)
  1996-03-30  0:00 ` Simon Wright
  1996-04-01  0:00 ` Laurent Guerby
  5 siblings, 5 replies; 80+ messages in thread
From: Robert A Duff @ 1996-03-29  0:00 UTC (permalink / raw)


In article <wnewmanDoxrCp.DKv@netcom.com>,
Bill Newman <wnewman@netcom.com> wrote:
>...  (I hope against hope that
> the tone of the answers will resemble the cross-language comparisons
> in _Ada as a Second Language_ more closely than it resembles the
> recent C++/Ada flamewar.)

;-)

Did you notice the recent Eiffel/Ada flamewar?  It was much more
reasonable -- not even a flamewar, really, but a technical argument.
It *is* possible!

> Does GNAT completely implement generics as defined in the standard?
> (I ask because I have heard that no compiler, G++ otherwise, has yet
> implemented C++ templates completely, and the G++ implementation
> caused me lots of hassles before 2.7.x, and still causes some hassles
> now.)
> 
> I *assume* that GNAT supports exceptions completely since they're an integral
> part of the language and I didn't see any disclaimers, but since AFAIK G++
> doesn't do them very well, I'd like to double-check: how well does
> GNAT do exceptions?

Generics and exceptions are both standard parts of Ada, so anything that
calls itself an Ada compiler implements them.  Furthermore, these
features have been around since Ada 83 (although a few bells and
whistles were added in Ada 95).  Any compiler can have bugs, of course.
But the situation with C++, where templates and exceptions are pretty
new, and not officially standardized, isn't true of Ada.

> I didn't notice anything about garbage collection in the GNAT docs, so
> I assume it doesn't support it.  Will GNAT support GC in the
> foreseeable future?

GNAT does not support GC.  I believe somebody is working on adding GC to
GNAT.  Also, the Intermetrics Ada compiler supports GC.

> How well does GDB work with GNAT output?

I've found it to be somewhat painful.  However, it does work, and the
Ada support is being improved.

>...Is it possible to get GDB to
> interactively call arbitrary procedures/functions from GNAT-generated
> code?

Yes, I do that a lot.

> Is there any way in Ada to iterate abstractly over the contents of a 
> container,...

Several ways:

    - Write a generic procedure Iterate, which takes a formal
      procedure representing the body of the loop -- that is,
      the formal procedure says what to do for each element.

    - Write a procedure that takes an access-to-procedure as
      a parameter.  In standard Ada, this only works if the
      procedure being passed in is non-nested, which makes this
      technique fairly useless.  In GNAT, you can "cheat", by
      using 'Unrestricted_Access, but that's not standard Ada.

    - Write an Iterator class.  Define any particular loop
      by deriving from that, and overriding the Do_One_Element
      method, or whatever.  I think there's an example in the
      Rationale.

    - For each data structure, define Start, Done, Current_Element,
      and Get_Next operations.  Or some variation on this.

None of these is as pretty as the Sather solution, but they achieve the
main goal, which is to have the module that defines the data structure
define how to loop, and the clients define what they do at each
iteration.

>... i.e. without writing each loop in a way which depends
> strongly on the implementation of the container?  I know I 
> could define a container class Foo_Basket_Type which would 
> let me do something like
>     for I = 1 .. Size(Foo_Basket) loop -- class implemented as array
>        Sum := Sum + Bletchery(Element(Foo_Basket, I));
>     end loop;
> or 
>     I : Foo_Basket_Iterator_Type := Head(Foo_Basket);
>     ..
>     while not Is_Done(I) loop -- class implemented as list
>        Sum := Sum + Bletchery(dereference I);
>        I := Next(I);
>     end loop;

It seems like the above does not have to imply "class implemented as
list".  So just write all your loops like that, whether the
implementation is a list, or an array, or whatever.  You can do this in
most languages.  This is what I was suggesting in the fourth item above.

> but what I'd really like to is something like 
>     for each I in Foo_Basket loop -- don't much care how class is implemented
>        Sum := Sum + Bletchery(dereference I);
>     end loop;
> I understand that in languages like Sather, I could do this without
> macros.  In G++ I can come pretty close to this with CPP macros:
>     FOR_EACH(i, foo_basket)
>       sum += bletchery(*i);
> In standard C++ (without the G++ `typeof' operator) I'd write something like
>     FOR_EACH(i, Foo_Basket::Iterator, foo_basket)
>       sum += bletchery(*i);
> I use this idiom a lot -- about once per 40 lines of code in my
> current project.  I much prefer it to the alternative of making my
> code depend on the implementation of the container.

I don't agree that the only alternative to macros is to break the
abstraction.  The techniques I mentioned above also achieve your goal,
although they are admittedly somewhat ugly.  Macros can add syntactic
sugar to those techniques, but don't fundamentally change things.

>...Is there any way of
> doing something like this in Ada?  (I imagine there is, since the
> alternatives look unnecessarily difficult to maintain.)

I'll note that the alternatives I gave could be done in C++, too,
without macros.

> Due in part to my problems with GDB mentioned above, my C++ programs
> tend to contain a lot of calls to macros MUTTER1, MUTTER2,
> etc. defined either as no-op (for low levels of verbosity) or as
>    #define MUTTER1(x) do { cerr << mutter_prefix << x << '\n'; } while (false)
> so that I can write things like 
>    MUTTER1("done with sampling, w = " << w << ", table = " << table);
> concisely.  It's my impression that Ada's I/O facilities make it hard
> to do trivial things like this concisely.  Is this wrong?  I don't really
> want to have to write 
>    if (Global_Verbosity >= 1) then
>      Write_Mutter_Indent(Global_Mutter);
>      Write(Global_Mutter, "done with sampling, W = ");
>      Write(Global_Mutter, W);
>      Write(Global_Mutter, ", table = ");
>      Write(Global_Mutter, Table);
>      Write_Line(Global_Error); -- silly typo encouraged by excess verbosity
>    end if;
> for a simple statement like the one above.

The usual thing is to write something like:

    Put_Line("done with sampling, W = " & Image(W)
           & ", table = " & Image(Table));

For simple data types, there is the 'Image attribute.  For others, you
have to write your own Image function, which converts it to a string.
You said you wanted to do that anyway.  If you don't like
Text_IO.Put_Line, write your own: Two procedures for each verbosity
level, and one of those two can add the new-line.  Or maybe you always
want a new-line.  Or maybe the verbosity should be passed as a
parameter, with a default.  I can imagine many variations.

Some people go even further, and define overloaded versions of "&" that
do both the concatenation and the conversion-to-string.  Seems like
overkill to me, but some people like it.

> When I make two different instantiations of a generic package with the
> same arguments, I understand the compiler treats them formally as two
> different packages, which is OK with me.  However, I'd appreciate
> knowing the compiler wouldn't actually output two redundant copies of
> the corresponding (identical?) machine code, but instead share the
> code.  I saw somewhere that the compiler is given considerable freedom
> to share one instantiation between several arguments if it thinks it's
> appropriate, which is also OK with me.  However, I haven't seen any
> guarantee that the compiler won't output redundant copies for
> instantiations with identical arguments.  Is there such a guarantee?

No.  Some compilers do it, some don't.  Currently, GNAT does not.

If you want to avoid code bloat, you can usually arrange for *most* of
the code in a generic to be separated out into a non-generic package.
Sometimes, this requires low-level hacks, but the low-level hacks are
hidden inside the generic and its helper package, so they need not
concern clients.

> My examples of `for each' and `mutter' above share a common feature:
> I'd like to abstract away a common pattern so that I only need to type
> each argument once, and I haven't figured out how to do it in Ada.

Macros can be used to achieve that, but in many cases, so can
procedures, generics, Sather's iterators, etc.  IMHO, the more
restricted features are generally better than macros.  Macros are more
powerful, but also more difficult to understand.  TeX is a language that
uses macros for just about everything, and I find TeX code to be totally
incomprehensible, despite the fact that I've read the TeX Book 4 times.

>...I
> don't object to the rest of the verbosity I have seen in Ada: I can
> see that it might make programs more readable, and since it's checked
> by the compiler it shouldn't make programs significantly harder to
> maintain.  However, the kind of verbosity required to hand-code
> patterns like `for each' and `mutter' does not seem to make code more
> readable, and since it is not checked by the compiler I'm afraid it
> might cause maintenance problems by making it possible to add bugs by
> changing some but not all occurrences of the redundant terms.  Are
> there ways (e.g.  in the cases described above, or other cases that I
> haven't thought of yet) in which Ada will force me to do this kind of
> thing where C++ wouldn't?  If so, is this a bad thing (as I suspect),
> or is it a positive feature in some sense that I haven't figured out,
> or is it just the price we pay for the benefit of being guaranteed
> that no one has abused macros in the program?

I think the last phrase ("price we pay...") is accurate.  I actually
think macros would be nice to have in some rare cases.  But in *most*
cases, if you're using macros, that's an indication of a language flaw
-- you didn't have a higher-level feature that got the job done, so you
had to resort to macros.

You'll find a lot of anti-macro fanatics in the Ada world.

> Why doesn't Ada 95 allow declarations to be interspersed with ordinary
> statements as C++ does?  (Or does it?  _Ada as a Second Language_ is a
> big book!)  It seems to me that the C++ approach is a small but
> definite win.  Does it interact very badly somehow with all those
> guarantees on elaboration order?

To intersperse declarations, you have to use a block_statement, like
this:

    for I in Some_String'Range loop -- I wish I could easily use an iterator here ;-)
        declare
            X: constant Character := Some_String(I);
        begin
            ...
        end;
    end loop;

To me, the "declare", "begin", and "end" are just useless verbosity.
I prefer the C++ rule.

As to WHY the Ada syntax is this way, I'm not sure.  It's partly because
it was inherited from Pascal.  In Pascal, "begin" separates purely
compile-time declarative stuff from purely run-time algorithmic stuff,
so the "begin" makes some conceptual sense.  But in Ada, the
declarations can do all kinds of run-time stuff, so putting "begin" in
between declarations and statements makes less sense.  Pascal doesn't
have block_statements, but the syntax of Ada's block_statements comes
from the same Pascal heritage.

The "begin" also makes a difference for exception handling and tasking.
But I don't find these reasons compelling, either.

> Someone remarked -- in this newsgroup recently IIRC -- that macros
> were explicitly disallowed in the design goals for Ada (Ada 83?),
> since they make programs hard to understand.  I don't remember the
> exact wording, but as I remember the key reason was that you would
> never know what was a macro and what wasn't without reading the entire
> program hunting for macro definitions.  (I searched for `macro' in the
> Rationale without success, so I'm just going on speculation and dim
> memory here.)  It seems to me that that is a funny objection: most
> macro processors these days don't require a characteristic pattern to
> introduce macro expansions, but as far as I can tell there's no reason
> that you couldn't restrict macro expansion to e.g. patterns preceded
> by the keyword `macro'.

I don't think this is the main reason people think macros are hard to
understand.  As you say, it would be easy to design the syntax so that a
macro invokation looks different from anything else.

Macros are generally hard to understand for other reasons, I think.
Part of the problem is the low-level character-based nature of macros.
But as you point out below, that's not true of all known macro
facilities.  Part of the problem is the lack of scoping rules -- if a
macro expansion refers to a global variable X, it's referring to
whatever X happens to be lying around at the point of the macro
expansion.  Contrast that with Ada's generics, where the names are bound
at the site of the generic itself.  Part of the problem is that you have
to imagine what the macro expands to in order to understand what it
does, and complex code trasformations are hard to imagine accurately.
TeX macros have additional problems, like the fact that you can't tell
at the call site how many arguments are being passed -- in fact, you
can't in general tell until run time.

>...  Granted, C's macro facilities are fairly
> disgusting and inconsistent with the design goals of Ada, but it seems
> to me a macro facility (or `generic syntax' facility?) more consistent
> with Ada could be useful -- perhaps something closer to Scheme's
> `hygienic macros' than to C's macro preprocessor.  Some restrictions
> (e.g. only allowing macros to expand into expressions, sequences of
> statements, or sequences of declarations, and not into arbitrary textual
> strings) and perhaps importing from Lisp the idea that macros are
> transformations on patterns of language tokens and expressions, rather
> than transformations on patterns of characters) could do a pretty
> decent job of discouraging people from writing truly screwy macros

I agree.  Lisp macros are much less painful than character-level macros.

> without forbidding useful macros like `For_Each' or (to generalize
> `MUTTER') `Write_Sequence' (sending a bunch of items to the same
> stream, and using the language's overloading facility to sort out
> which version of the Write procedure to call for each).

If you complain that you want macros to do For_Each, I'll answer by
giving you iterators, not by giving you macros.  Same for Write_Sequence
-- better to use a procedure call than a macro call, IMHO.

>...Would this
> have been a good idea, except perhaps that the language is too
> complicated already, or would this be a bad idea for some more
> fundamental reason?

I think most Ada people would say that macros inherently cause
hard-to-understand code, and therefore should be abolished.  I don't
agree.  I would like to have a macro facility (at a higher level than
character-level), but I would use it rarely.

> Finally, I found it intriguing when someone (somewhere in the endless
> C++ vs. Ada thread) described a program which used exceptions to
> convert runtime errors to `graceful degradation' so successfully that
> the program (for fire control?!) continued operating more-or-less
> correctly despite numerous bugs. ...

I'm not impressed by that example.  It's true that exceptions can be
useful in designing a fault-tolerant system, but fault-tolerance is
still very hard, and needs to be very carefully designed.  For *most*
programs, the primary purpose of exceptions, most of the time, is to
detect bugs as early as possible, and the response is to fix the bug,
not to allow the program to muddle along in a confused state.

- Bob

P.S. I hope you enjoy Ada.  Why not get a copy of GNAT, and try it out?




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

* Iterators (was Re: some questions re. Ada/GNAT from a C++/GCC user)
  1996-03-29  0:00 ` Robert A Duff
  1996-03-29  0:00   ` Brian Rogoff
@ 1996-03-30  0:00   ` Robert I. Eachus
  1996-03-31  0:00     ` Mike Young
       [not found]   ` <4jlj79$h1k@Nntp1.mcs.net>
                     ` (2 subsequent siblings)
  4 siblings, 1 reply; 80+ messages in thread
From: Robert I. Eachus @ 1996-03-30  0:00 UTC (permalink / raw)


In article <Dp1oAw.7Cz@world.std.com> bobduff@world.std.com (Robert A Duff) writes:

  > > Is there any way in Ada to iterate abstractly over the contents of a 
  > > container,...

  > Several ways:

  >     - Write a generic procedure Iterate, which takes a formal
  >	 procedure representing the body of the loop -- that is,
  >	 the formal procedure says what to do for each element.

  Funny you should mention it...  Right now I have two projects where
I am dealing with a large mass (1000s) of source files.  I need to run
some tools over the code.  The choice was between Ada and a shell
script...I chose Ada:

generic
  with procedure To_Do(File: in String);
procedure Iterate_Files(Pattern: in String := "*";
                        Directory: in String := "");
-- This generic iterates over all files in a directory matching Pattern and
-- calls To_Do once for each such directory entry.  If a file has several
-- links in the directory To_Do will be called once for each.

  (The body for SunAda/VADS is tacked on at the end of this
message. It isn't all that complex. If someone wants to rewrite for
GNAT and post, feel free, but there may be a lot of GNAT versions
needed,  I don't know if it is possible to share code between
different operating systems.)

   Two observations.  First, the generic procedure approach is
certainly right for this case.  Second, the VADS run-time provided a
call for freeing A_Strings, but not for find_files.find_file_rec, so I
put in the call for the A_Strings case only.  If I felt that there
would be hundreds of calls with different patterns, I probably would
have gone through the pain of "fixing" the find_files package by
adding the missing functionality.  But in my case, the one call to
A_Strings.Free(Next) is enough garbage collection for all practical
purposes...the call in the exception handler is for style more than
anything else.

    And I know that this is something I have said before, but this
particular example is a good one.  The primary argument in favor of
compilers which support general garbage collection is that the end
user shouldn't have to worry about garbage collection.  But in this
example 1) the user of the package sees no garbage ever and 2)
managing memory "right" was an extremely small effort when writing the
generic.  Even if all Ada compilers had generalized garbage
collectors, the right choice here would be to build a procedure which
predictably cleaned up after itself rather than warning the user that
there would be lots of garbage generated which might affect global
performance.


					Robert I. Eachus

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


----------------------------------------------------------------------------
-- generic
--   with procedure To_Do(File: in String);
with A_Strings;
with File_Names;
procedure Iterate_Files(Pattern: in String := "*";
                        Directory: in String := "") is
  Pat: A_Strings.A_String;
  Handle: File_Names.Find_File_Info;
  Next: A_Strings.A_String;
begin

  if Directory = "" 
  then Pat := A_Strings.To_A(Pattern);
  else Pat := A_Strings.To_A(Directory & "/" & Pattern);
  end if;

  Handle := File_Names.Init_Find_File(Pat);
  loop
     Next := File_Names.Find_File(Handle);
     if Directory = "" and then Next.s(1..2) = "./"
     then To_Do(Next.s(3..Next.len));
     else To_Do(Next.s);
     end if;
     A_Strings.Free(Next);
  end loop;
exception
  when File_Names.No_More_Files => A_Strings.Free(Pat);
end Iterate_Files;

-----------------------------------------------------------------------------
--

					Robert I. Eachus

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




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

* Re: some questions re. Ada/GNAT from a C++/GCC user
  1996-03-29  0:00     ` John G. Volan
  1996-03-30  0:00       ` Robert A Duff
@ 1996-03-30  0:00       ` Mike Young
  1996-03-30  0:00         ` Ted Dennison
  1996-04-01  0:00       ` Norman H. Cohen
  2 siblings, 1 reply; 80+ messages in thread
From: Mike Young @ 1996-03-30  0:00 UTC (permalink / raw)


John G. Volan wrote:
> 
> In article <315AC5E7.3A77@escmail.orl.mmc.com>
> Ted Dennison, dennison@escmail.orl.mmc.com writes:
> 
> >If you want to declare variables later in the code just
> >use a declare block:
> >   declare
> >      Var1 : My_Type;
> >      ...
> >   begin
> >      Var1 := My_Value;
> >      ...
> >   end;
> >
> >This has the added benefit of limiting the scope of Var1 to the
> >begin..end  block of code.
> 
> There is another benefit, but it isn't obvious unless you take into
> account the concurrency aspects of Ada:
> 
> A C programmer would complain about having to introduce an extra
> begin/end block in order to introduce variables.  Isn't the scope
> of a variable well-defined in C, even without this extra baggage?
> It just extends from the declaration to the end of whatever block
> you're already in.
> 
> An Ada programmer would counter that, in Ada, some variables might be
> instances of _task_ types.  By definition, a task object gets created
> when its declaration is elaborated, but it does not get _activated_
> (i.e., it doesn't start executing its statements) until you hit the
> "begin" of the enclosing block.  This guarantees that everything in 

  [ ... some worthy thoughts snipped ... ]

> 
> C, of course, has no concurrency abstractions built in (nor does C++,
> last time I checked), so, gee, I guess this isn't an issue in C. :-)

============
Hmmmm. Lacking a language based synch method doesn't mean we always go 
straight to the semaphore library. I commonly use thin wrappers to guard 
precious resources -- for example: mutexes, semaphores, files, 
or database sessions. Local scoping allows precise control of lifetimes, 
and ensure release even when exceptions are possible:

void foo ()
{
   //-- some setup code here, perhaps
   ...
   {               // local scope
     LocalMutex<BazType> foo(mutexHandle);
     BazType & bar = foo.openResource();
     ...           // use the protected resource
   }               // end local scope. LocalMutex releases the resource
   ...
}




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

* Re: some questions re. Ada/GNAT from a C++/GCC user
  1996-03-27  0:00 some questions re. Ada/GNAT from a C++/GCC user Bill Newman
                   ` (3 preceding siblings ...)
  1996-03-29  0:00 ` Robert A Duff
@ 1996-03-30  0:00 ` Simon Wright
  1996-04-01  0:00 ` Laurent Guerby
  5 siblings, 0 replies; 80+ messages in thread
From: Simon Wright @ 1996-03-30  0:00 UTC (permalink / raw)


In article <4jhe1v$m0g@dayuc.dayton.saic.com> John G. Volan <John_Volan@ccmail.dayton.saic.com> writes:
[...]
> A C programmer would complain about having to introduce an extra
> begin/end block in order to introduce variables.  Isn't the scope
> of a variable well-defined in C, even without this extra baggage?
> It just extends from the declaration to the end of whatever block
> you're already in.
> 
> An Ada programmer would counter that, in Ada, some variables might be
> instances of _task_ types.  By definition, a task object gets created
[...]

I would be quite likely to use declare blocks to introduce types and
instantiations, for example where the size of a data structure wasn't
known until that point.

-- 
Simon Wright                    Work Email: simon.j.wright@gecm.com
Ferranti Naval Systems                     Voice: +44(0)1705-701778
GEC-Marconi S3I Combat Systems Division      FAX: +44(0)1705-701800




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

* Re: some questions re. Ada/GNAT from a C++/GCC user
  1996-03-30  0:00       ` Mike Young
@ 1996-03-30  0:00         ` Ted Dennison
  1996-03-31  0:00           ` Mike Young
  0 siblings, 1 reply; 80+ messages in thread
From: Ted Dennison @ 1996-03-30  0:00 UTC (permalink / raw)


Mike Young wrote:
> 
> Hmmmm. Lacking a language based synch method doesn't mean we always go
> straight to the semaphore library. I commonly use thin wrappers to guard
> precious resources -- for example: mutexes, semaphores, files,
> or database sessions. Local scoping allows precise control of lifetimes,
> and ensure release even when exceptions are possible:

OK. Now what do you do if you have to port that code to DOS? :-)

-- 
T.E.D.          
                |  Work - mailto:dennison@escmail.orl.mmc.com  |
                |  Home - mailto:dennison@iag.net              |
                |  URL  - http://www.iag.net/~dennison         |




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

* Re: some questions re. Ada/GNAT from a C++/GCC user
  1996-03-29  0:00     ` John G. Volan
@ 1996-03-30  0:00       ` Robert A Duff
  1996-03-31  0:00         ` John G. Volan
  1996-03-31  0:00         ` Robert Dewar
  1996-03-30  0:00       ` Mike Young
  1996-04-01  0:00       ` Norman H. Cohen
  2 siblings, 2 replies; 80+ messages in thread
From: Robert A Duff @ 1996-03-30  0:00 UTC (permalink / raw)


In article <4jhe1v$m0g@dayuc.dayton.saic.com>,
John G. Volan  <John_Volan@ccmail.dayton.saic.com> wrote:
>A C programmer would complain about having to introduce an extra
>begin/end block in order to introduce variables.  Isn't the scope
>of a variable well-defined in C, even without this extra baggage?
>It just extends from the declaration to the end of whatever block
>you're already in.

Well, at least one Ada programmer (namely, me) would agree with the C
programmer in this case.

>An Ada programmer would counter that, in Ada, some variables might be
>instances of _task_ types. ...

Sorry to keep disagreeing with you, John, but this seems like the tail
wagging the dog.  If the reason I'm required to add 3 extra junk lines
of code, just to declare a simple variable, is because of tasks, then
that's just poor language design.  If you need a place to activate the
tasks, fine, put in an "Activate_Tasks_Here" statement.  Don't
complicate the job for some poor guy who has no tasks, and just wants to
declare an Integer or String variable.

- Bob




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

* Re: some questions re. Ada/GNAT from a C++/GCC user
  1996-03-29  0:00   ` Robert A Duff
@ 1996-03-30  0:00     ` Richard Pitre
  1996-03-30  0:00       ` Robert A Duff
  1996-04-02  0:00       ` Robert I. Eachus
  0 siblings, 2 replies; 80+ messages in thread
From: Richard Pitre @ 1996-03-30  0:00 UTC (permalink / raw)


In article <Dp1sMM.23q@world.std.com> bobduff@world.std.com (Robert A Duff)  
writes:
> In article <leschkes.828053356@ferret>,
> Scott Leschke <leschkes@ferret.cig.mot.com> wrote:
> >My first question would be, why do you want redundant instantiations.
> 
> Usually you don't.  But you might declare a type Apple_Count, and not
> know whether anybody wants to do I/O on it.  Then one client does, and
> instantiates Text_IO.Integer_IO on Apple_Count.  And another client
> independently does the same.  One might like to share the code of the
> two instantiations.
> 
> But, as Bill Newman pointed out, the more important thing is when you
> have Apple_Count and Orange_Count, which are logically distinct, but
> happen to share the same hardware representation.  That's a more
> important case where you'd like shared generic code.
> 
> Shared generic code isn't easy to implement in general, but many Ada 83
> compilers support it, at least for some kinds of generics.  The problem
> is that you can't count on it in portable code, so you have to do it "by
> hand".  That's not so awful, I suppose -- at least the ugly parts are
> buried in one place.
> 
> >You can use a block statement.  This is different than C++ in the sense
> >that objects declared within the block are only in existence within the
> >block and are finalized at the end.
> 
> No, I think this is exactly the same in C++ and Ada.  See r.6.7 of The
> C++ Programming Language, Second Edition, by Bjarne Stroustrup.  That
> is, if you declare an object in C++ in a local sequence of statements,
> it will be finalized at the end of that sequence.  
> The only difference
> is that Ada requires some extra syntactic baggage.
> 
> >declare
> >   Obj : Pkg.SomeType;
> >begin
> >   Pkg.Operation (Object => Obj);
> >   -- Other stuff
> >exception
> >   when Pkg.Some_Exception =>
> >
> >      Do_Something;
> >end;
> 
> The exception handler makes this look reasonable, but in the usual case,
> where there's no need for any exception handling, and you just want to
> declare Obj, the extra 3 lines of code ("declare", "begin", "end") are
> just an annoyance.

I thing of "begin" and "end" are visual cues and I prefer them to eyeburner
baggage like "{" and "}".

richard




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

* Re: some questions re. Ada/GNAT from a C++/GCC user
  1996-03-30  0:00     ` Richard Pitre
@ 1996-03-30  0:00       ` Robert A Duff
  1996-03-31  0:00         ` AdaWorks
                           ` (2 more replies)
  1996-04-02  0:00       ` Robert I. Eachus
  1 sibling, 3 replies; 80+ messages in thread
From: Robert A Duff @ 1996-03-30  0:00 UTC (permalink / raw)


In article <4jjul6$637@ra.nrl.navy.mil>,
Richard Pitre <pitre@n5160d.nrl.navy.mil> wrote:
>I thing of "begin" and "end" are visual cues and I prefer them to eyeburner
>baggage like "{" and "}".

You miss the point.  C++ doesn't require "{" and "}" in this case -- the
case we're talking about is where no visual cues are necessary.  I can
understand that one might like "begin" better than "{", but this is a
case where C++ requires nothing at all, and Ada requires "declare",
"begin", and "end", for no good reason.

- Bob




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

* Re: Iterators (was Re: some questions re. Ada/GNAT from a C++/GCC user)
  1996-03-31  0:00     ` Mike Young
@ 1996-03-31  0:00       ` Fergus Henderson
  0 siblings, 0 replies; 80+ messages in thread
From: Fergus Henderson @ 1996-03-31  0:00 UTC (permalink / raw)


mikey@mcs.com (Mike Young) writes:

>eachus@spectre.mitre.org> says...
>>The choice was between Ada and a shell script...I chose Ada:
>>
>>generic
>>  with procedure To_Do(File: in String);
>>procedure Iterate_Files(Pattern: in String := "*";
>>                        Directory: in String := "");
>>-- This generic iterates over all files in a directory matching Pattern and
>>-- calls To_Do once for each such directory entry.  If a file has several
>>-- links in the directory To_Do will be called once for each.
>
>=========
>I don't know about that, Robert. The equivalent shell script would be:
>
>#---- iterateFiles
>fileop=$1;shift
>for file in $*; do $fileop $file; done

No, that shell script, like the vast majority of shell scripts around,
is buggy.  Writing correct, portable shell scripts is actually pretty tricky.

Here's a better version:

	fileop="$1"; shift
	case $# in
		0) ;;
		*) for file in "$@"; do $fileop "$file"; done ;;
	esac

Now just be careful with the quoting when you invoke it...

> You are not restricted to procedures in that same program, all system
> utilities are directly available, you don't need to rebuild to add new
> features, and you need not have bothered with your 50 lines of code.
> All are big wins, IMO.

Oh, certainly shell scripts are extremely useful. 
But it helps to be aware of their drawbacks as well as their advantages.

--
Fergus Henderson <fjh@cs.mu.oz.au>   |  "I have always known that the pursuit
WWW: <http://www.cs.mu.oz.au/~fjh>   |  of excellence is a lethal habit"
PGP: finger fjh@128.250.37.3         |     -- the last words of T. S. Garp.




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

* Re: some questions re. Ada/GNAT from a C++/GCC user
  1996-03-30  0:00       ` Robert A Duff
@ 1996-03-31  0:00         ` John G. Volan
  1996-03-31  0:00           ` Mike Young
                             ` (2 more replies)
  1996-03-31  0:00         ` Robert Dewar
  1 sibling, 3 replies; 80+ messages in thread
From: John G. Volan @ 1996-03-31  0:00 UTC (permalink / raw)


In article <Dp3G4u.KEA@world.std.com> Robert A Duff, bobduff@world.std.com
writes:
>In article <4jhe1v$m0g@dayuc.dayton.saic.com>,
>John G. Volan  <John_Volan@ccmail.dayton.saic.com> wrote:
>>A C programmer would complain about having to introduce an extra
>>begin/end block in order to introduce variables.  Isn't the scope
>>of a variable well-defined in C, even without this extra baggage?
>>It just extends from the declaration to the end of whatever block
>>you're already in.
>
>Well, at least one Ada programmer (namely, me) would agree with the C
>programmer in this case.
>
>>An Ada programmer would counter that, in Ada, some variables might be
>>instances of _task_ types. ...
>
>Sorry to keep disagreeing with you, John, ...

Don't be.  Keeps things interesting! :-)

>...but this seems like the tail
>wagging the dog.  If the reason I'm required to add 3 extra junk lines
>of code, just to declare a simple variable, is because of tasks, then
>that's just poor language design.  If you need a place to activate the
>tasks, fine, put in an "Activate_Tasks_Here" statement.  Don't
>complicate the job for some poor guy who has no tasks, and just wants to
>declare an Integer or String variable.

Okay, maybe the tasking issue was a bit of a stretch.  With respect to
tasks, I still feel that it's important to be able to distinguish the
difference between elaboration and execution, but I agree that this
doesn't give us a _comprehensive_ rationale for for Ada's strict rules
separating declarations from statements.

Okay, then, can anybody who was in on the Ada83/Ada95 design process
give us a more comprehensive rationale for this strict separation, one
consequence of which being the need for declare statements?  Warning: It
may not be sufficient to say that we need the blocks in order to define
the scope of the declarations, since as I pointed out, it might be
possible to come up with a coherent definition of the scope of a
declaration even with C's scheme (C manages it somehow, after all).

One difficulty I see with intermingling declarations and statements is
how to interpret declarations within conditional or iterative constructs.
For instance:

    -- This is NOT Ada, this is CRAPOLA (C-Reminiscent Ada-like Perversion
    -- Of Language Aspects)  :-) :
    begin
        ...
        if Smaller then
            X : Integer;
            ...
        elsif Bigger then
            X : Long_Integer;
            ...
        end if;
        ...
        -- is X in scope here, and if so, what the heck is it?
        ...
        
        ... loop
            Y : Integer;
            Get (Y);
            type A is array (1 .. Y) of Integer;
            package P is new Generic_P (A);
            -- do these things get elaborated & destroyed every iteration?
            ...
        end loop ... ;
        ...
        -- are Y, A, and P still in scope here?
    end ... ;
    

I assume that the only thing that would make sense would be to treat
every structured statement as the moral equivalent of a begin/end.
So wherever Ada has a sequence_of_statements, CRAPOLA would have a 
"block", in which statements and declarations can be intermingled,
but the scope of the declarations would be limited to that "block".
So the two X's above would only be in scope within their respective
then-parts, and Y, A, and P would only be in scope for one iteration
of the loop (i.e., they'd be created and destroyed with each iteration).

(I may not be able to follow up for a while folks, going on travel
tomorrow ... hey, don't everybody cheer at once! :-)

------------------------------------------------------------------------
Internet.Usenet.Put_Signature
( Name => "John G. Volan", E_Mail => "John_Volan@dayton.saic.com",
  Favorite_Slogan => "Ada95: The *FIRST* International-Standard OOPL",
  Humorous_Disclaimer => "These opinions are undefined by SAIC, so" &
    "any use would be erroneous ... or is that a bounded error now?" );
------------------------------------------------------------------------




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

* Re: some questions re. Ada/GNAT from a C++/GCC user
  1996-03-31  0:00         ` John G. Volan
@ 1996-03-31  0:00           ` Mike Young
  1996-04-02  0:00             ` Glenn H. Porter
  1996-04-01  0:00           ` Robert A Duff
  1996-04-01  0:00           ` Bruce.Conroy
  2 siblings, 1 reply; 80+ messages in thread
From: Mike Young @ 1996-03-31  0:00 UTC (permalink / raw)


John G. Volan wrote:
> 
> One difficulty I see with intermingling declarations and statements is
> how to interpret declarations within conditional or iterative constructs.
> For instance:
> 
>     -- This is NOT Ada, this is CRAPOLA (C-Reminiscent Ada-like Perversion
>     -- Of Language Aspects)  :-) :
>     begin
>         ...
>         if Smaller then
>             X : Integer;
>             ...
>         elsif Bigger then
>             X : Long_Integer;
>             ...
>         end if;
>         ...
>         -- is X in scope here, and if so, what the heck is it?
>         ...

==========
(Cute acronym; not sure if I like it though. :)

The scoping rules in C++ are quite simple: the object comes into 
existence at the point of definition, and persists until the end of the 
enclosing scope. In your example, X would is created at its declaration 
(eg.: X : Long_Integer;), and is destroyed at the corresponding elsif or 
endif.

Sprinkling declarations willy-nilly throughout the code is more than a 
matter of convenience. Construction of objects sometimes entail 
non-trivial construction. Rather than force a two step process -- 
construction at declaration, and then subsequent initialization -- it is 
very common to declare the object "just-in-time," and combine the two 
steps. This frees the object designer from allowing for an undefined 
state, which would exist if construction were separated from 
initialization.

Personally, I like it this way. It's very easy to verify that all 
objects are correctly initialized before use when all objects are 
initialized at their points of construction. The object does not exist 
before it is initialized.

Mike.




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

* Re: some questions re. Ada/GNAT from a C++/GCC user
  1996-03-30  0:00       ` Robert A Duff
  1996-03-31  0:00         ` John G. Volan
@ 1996-03-31  0:00         ` Robert Dewar
  1996-04-01  0:00           ` Norman H. Cohen
  1 sibling, 1 reply; 80+ messages in thread
From: Robert Dewar @ 1996-03-31  0:00 UTC (permalink / raw)


The trouble with mixing declarations and statements is that it blurs
the lines beween elaboration and execution, and can result in considerable
semantic confusion. Where for example would tasks be activated, and
what does

  if x then a : integer; ....

Yes, this can be given a meaning, but I don't think it is worth the
effort. Having programmed in Algol-68 a lot, this is one A68 feature
I can do without.





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

* Re: some questions re. Ada/GNAT from a C++/GCC user
  1996-03-30  0:00       ` Robert A Duff
@ 1996-03-31  0:00         ` AdaWorks
  1996-04-01  0:00           ` Robert A Duff
  1996-04-01  0:00         ` Richard A. O'Keefe
  1996-04-01  0:00         ` Robert Dewar
  2 siblings, 1 reply; 80+ messages in thread
From: AdaWorks @ 1996-03-31  0:00 UTC (permalink / raw)


Robert A Duff (bobduff@world.std.com) wrote:

: You miss the point.  C++ doesn't require "{" and "}" in this case -- the
: case we're talking about is where no visual cues are necessary.  I can
: understand that one might like "begin" better than "{", but this is a
: case where C++ requires nothing at all, and Ada requires "declare",
: "begin", and "end", for no good reason.

  Surely you inadvertantly omitted your smiley for the phrase, "for no
  good reason."

  Richard Riehle
  adaworks@netcom.com
-- 

richard@adaworks.com
AdaWorks Software Engineering
Suite 27
2555 Park Boulevard
Palo Alto, CA 94306
(415) 328-1815
FAX  328-1112




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

* Re: some questions re. Ada/GNAT from a C++/GCC user
  1996-03-30  0:00         ` Ted Dennison
@ 1996-03-31  0:00           ` Mike Young
  0 siblings, 0 replies; 80+ messages in thread
From: Mike Young @ 1996-03-31  0:00 UTC (permalink / raw)


In article <315D902C.6F7B@escmail.orl.mmc.com>, dennison@escmail.orl.mmc.com 
says...
>
>Mike Young wrote:
>> 
>> Hmmmm. Lacking a language based synch method doesn't mean we always go
>> straight to the semaphore library. I commonly use thin wrappers to guard
>> precious resources -- for example: mutexes, semaphores, files,
>> or database sessions. Local scoping allows precise control of lifetimes,
>> and ensure release even when exceptions are possible:
>
>OK. Now what do you do if you have to port that code to DOS? :-)

===========
I believe it would then be time to consider relocation. The code itself, 
however, requires only minor modifications: return successful immediately. The 
concept -- resource guarding in a local scope -- still applies. :)

Mike.





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

* Re: Iterators (was Re: some questions re. Ada/GNAT from a C++/GCC user)
  1996-03-30  0:00   ` Iterators (was Re: some questions re. Ada/GNAT from a C++/GCC user) Robert I. Eachus
@ 1996-03-31  0:00     ` Mike Young
  1996-03-31  0:00       ` Fergus Henderson
  0 siblings, 1 reply; 80+ messages in thread
From: Mike Young @ 1996-03-31  0:00 UTC (permalink / raw)


In article <EACHUS.96Mar29191146@spectre.mitre.org>, eachus@spectre.mitre.org> 
says...
>some tools over the code.  The choice was between Ada and a shell
>script...I chose Ada:
>
>generic
>  with procedure To_Do(File: in String);
>procedure Iterate_Files(Pattern: in String := "*";
>                        Directory: in String := "");
>-- This generic iterates over all files in a directory matching Pattern and
>-- calls To_Do once for each such directory entry.  If a file has several
>-- links in the directory To_Do will be called once for each.

=========
I don't know about that, Robert. The equivalent shell script would be:

#---- iterateFiles
fileop=$1;shift
for file in $*; do $fileop $file; done

You would call it thusly:

  iterateFiles 'head -n 3' *.ada >header.comments

or, directly, without a script:
  for file in *.ada; do head -n 3 $file; done >header.comments

or, in Windows NT or 95:
  for %i in (*.ada) do gnatchop -s -w %i

You are not restricted to procedures in that same program, all system utilities 
are directly available, you don't need to rebuild to add new features, and you 
need not have bothered with your 50 lines of code. All are big wins, IMO. You 
can always write small, easy to maintain, helper programs if available 
utilities don't do what you need.

>
>
>   Two observations.  First, the generic procedure approach is
>certainly right for this case.  Second, the VADS run-time provided a
>call for freeing A_Strings, but not for find_files.find_file_rec, so I

===========
Hmmm.

Mike.





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

* Re: some questions re. Ada/GNAT from a C++/GCC user
  1996-03-29  0:00     ` John G. Volan
  1996-03-30  0:00       ` Robert A Duff
  1996-03-30  0:00       ` Mike Young
@ 1996-04-01  0:00       ` Norman H. Cohen
  1996-04-01  0:00         ` Robert A Duff
  1996-04-01  0:00         ` Mike Young
  2 siblings, 2 replies; 80+ messages in thread
From: Norman H. Cohen @ 1996-04-01  0:00 UTC (permalink / raw)


In article <4jhe1v$m0g@dayuc.dayton.saic.com>, John G. Volan
<John_Volan@ccmail.dayton.saic.com> writes: 

|> An Ada programmer would counter that, in Ada, some variables might be
|> instances of _task_ types.  By definition, a task object gets created
|> when its declaration is elaborated, but it does not get _activated_
|> (i.e., it doesn't start executing its statements) until you hit the
|> "begin" of the enclosing block.  This guarantees that everything in the
|> enclosing declarative region is elaborated before any a task starts up.

No, it doesn't guarantee that, because a task can also be created by an
ALLOCATOR in the declarative part, in which case it is activated
immediately.  Program_Error results if the task is activated before its
task body has been elaborated, so the fact that declarations in a
declarative part are elaborated in order is sufficient to guarantee that
any declaration referred to by the task unit has already been elaborated
by the time a task of the corresponding task type is activated.

|> In fact, _all_ the tasks in a declarative region are activated by the
|> "begin", simultaneously.  (Or virtually simultaneously. The point is,
|> Ada does not prescribe any particular order of activation, and does not
|> preclude truly simultaneous activation if the underlying run time
|> environment can support it.)  In effect, if a declarative region
|> contains tasks, then there is a synchronization point at the "begin"
|> which the Ada programmer can rely on.

All these complicated rules about activation (which originated in Ada
83) are actually there just for the sake of exception handling: If any
declared task fails during activation (i.e., raises an exception during
the elaboration of its task body's declarative part), Tasking_Error will
be raised in the associated sequence of statements.  Placing the nominal
point of activation just after the "begin" in the frame containing the
declaration means that the exception handlers of that frame can catch the
exception.  (This would not be the case if the Tasking_Error were raised
in declarative part.) Forcing the master frame to wait until all tasks
declared in the frame have been activated (or have died trying) allows
Tasking_Error to be raised exactly once in the master if one or more
dependent tasks have failed to activate.

There are at least three ways the rules of Ada could have allowed the
creator of a task to be informed about an unhandled exception in a task
it has created: 

  (a) Not at all.  In other words, the created task could die unnoticed,
      like Eleanor Rigby.
  (b) Asynchronously.  An exception could be raised, at an unpredictable
      time and place, in the creating task.
  (c) Synchronously.  An exception could be raised, if at all, at a
      predicatable time and place in the creating task.

Jean Ichbiah correctly saw the chaos that could result from approach (b).
He chose approach (c) for an exception occuring while the child task was
still elaborating the declarative part of its task body and (a) for an
exception occuring once the child task entered the sequence of statements
of its task body.  The problem with applying approach (a) in the
declarative part is that there is no way to handle the exception before
the execution of the task body is abandoned, since the handlers in the
task body do not apply to the declarative part.  Once the child has
reached adulthood, and is capable of handling its own exceptions, its
parent is no longer responsible for its sins.  (Anyone who has debugged a
program in which a task raised an unhandled exception has quickly learned
the value of adding a handler such as

   when others =>
      Display_Error_Message
         ("Task of type XYZ abandoned due to unhandled exception.");

to each task body.)

--
Norman H. Cohen    ncohen@watson.ibm.com




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

* Re: some questions re. Ada/GNAT from a C++/GCC user
  1996-03-31  0:00         ` Robert Dewar
@ 1996-04-01  0:00           ` Norman H. Cohen
  0 siblings, 0 replies; 80+ messages in thread
From: Norman H. Cohen @ 1996-04-01  0:00 UTC (permalink / raw)


In article <dewar.828333135@schonberg>, dewar@cs.nyu.edu (Robert Dewar)
writes: 
|> The trouble with mixing declarations and statements is that it blurs
|> the lines beween elaboration and execution,

These lines are already blurred in Ada, because elaboration takes place
at run time, when the flow of control reaches a declaration.  Indeed,
RM95 3.1(11) says, "The process by which a cosntruct achieves its
run-time effect is called _execution_.  One of the terms execution,
elaboration, or evaluation is defined by this International Standard for
each construct that has a run-time effect." In other words, elaboration
is one kind of execution.  There was no such passage in RM83, resulting
in the need for such circumlocutions as "The foregoing is expressed in
terms of the process that is called execution; it applies equally to the
processes that are called evaluation and elaboration" (RM83 1.6(9)).

|>                                             and can result in considerable
|> semantic confusion. Where for example would tasks be activated, and
|> what does
|>
|>   if x then a : integer; ....
|>
|> Yes, this can be given a meaning, but I don't think it is worth the
|> effort.

Rather than allowing an aribtrary interleaving of declarations and
statements as in C++, we can simply replace the rule

   sequence_of_statements ::= statement {statement}

with

   sequence_of_statements ::=
      {declarative_item} statement {statement}

and add the rule that the execution of a sequence of statements consists
of the elaboration of its declarative part followed by the execution of
its statements.  In other words,

   if P then
      X: constant Integer := ...;
      ...
      Y := X;
   else
      X: constant Integer := ...;
      ...
      Z:= X;
   end if;

becomes just a lightweight equivalent of

   if P then
      declare
         X: constant Integer := ...;
      begin
         ...
         Y := X;
      end
   else
      declare
         X: constant Integer := ...;
      begin
         ...
         Z:= X;
      end;
   end if;

(Note that if you want exception handlers inside the if statement, an
explicit block statement is still required.)

|>         Having programmed in Algol-68 a lot, this is one A68 feature
|> I can do without.

Actually, this is one aspect of C that I actually LIKE.  I always use {
and } brackets for my C compound statements to avoid accidently changing

   if (P)
      w = x;

to

   if (P)
      w = x;
      y = z;    /* Oh, dear.  Fooled by the indentation */

later.  Thus, at no extra notational cost, if a variable is to be used
only within one branch of an if statement, for example, I can declare it
locally to that if statement.  Besides making the code more readable by
bringing declarations textually closer to statements that use them, this
makes it much easier to move chunks of code around, e.g. to move part of
one function definition that has grown too large into a function
definition of its own, because a chunk of text becomes more independent
of the surrounding context.

I don't find myself doing the same thing (with block statements) in Ada
because the notation is too heavy (especially when set in bolface ;-) ).
For me at least, the lack of a shorthand equivalent is a psychological
barrier to localizing declarations.  I believe that if the shorthand were
allowed, many programmers would write more readable Ada code.

--
Norman H. Cohen    ncohen@watson.ibm.com




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

* Re: some questions re. Ada/GNAT from a C++/GCC user
  1996-04-01  0:00     ` Mark A Biggar
@ 1996-04-01  0:00       ` Robert A Duff
  0 siblings, 0 replies; 80+ messages in thread
From: Robert A Duff @ 1996-04-01  0:00 UTC (permalink / raw)


In article <4jovi9$c2b@wdl1.wdl.loral.com>,
Mark A Biggar <mab@dst17.wdl.loral.com> wrote:
>Yeah, 2 reasons.  The first goes clear back to the original Ada Strawman
>documnt which calls for a clear distinction between declarations and code
>statements.

But Ada does *not* have such a clear distinction.  Declarations do lots
of stuff at run time, in Ada.  In Pascal, the "begin" makes sense --
that's where execution starts; everything before that is purely
compile-time declarative stuff.  Not true in Ada.  If you're dealing
with Strings, for example, you often end up with *most* of the run-time
algorithm *before* the "begin", because Ada's Strings can't change size,
so you need to build up the result by declaring objects (usually
constants) during elaboration.

>...  The second reason is that is also the method of wraping an
>exception handler around soem arbitary piece of code:

Nah, this doesn't explain why I need extra syntax for very local
variables.  It explains why I need extra syntax when there are exception
handlers, but there usually aren't.  So this is another case of the tail
wagging the dog.  It's fine to have extra syntax to delimit the region
in which an exception handler applies, but that shouldn't complicate
things for somebody who just wants to declare a variable, and doesn't
want any exception handling.

- Bob




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

* Re: some questions re. Ada/GNAT from a C++/GCC user
  1996-04-01  0:00           ` Robert A Duff
@ 1996-04-01  0:00             ` Ken Garlington
  1996-04-01  0:00               ` Robert A Duff
  1996-04-01  0:00             ` Norman H. Cohen
  1996-04-01  0:00             ` AdaWorks
  2 siblings, 1 reply; 80+ messages in thread
From: Ken Garlington @ 1996-04-01  0:00 UTC (permalink / raw)


Robert A Duff wrote:
> 
> I really do think the extra
> verbosity damages readability, and there's "no good reason" for it.

I do think "declare" is useful, if you name the declare block. This gives you
an easy way to find the end of the scope, even in the presence of nested scopes.
Of course, an alternative would be to have the ability to name all constructs that 
would introduce a scope. Something like?

a_scope: if foo > bar then

   a: integer := 7;

   foo := foo * a;

else a_scope

  b: integer := 9;

  bar := bar * b;

end if a_scope;

You could do this with comments, of course, but then you lose the compiler 
checking...




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

* Re: some questions re. Ada/GNAT from a C++/GCC user
       [not found]   ` <4jlj79$h1k@Nntp1.mcs.net>
@ 1996-04-01  0:00     ` Robert A Duff
  1996-04-02  0:00       ` Kevin Cline
  0 siblings, 1 reply; 80+ messages in thread
From: Robert A Duff @ 1996-04-01  0:00 UTC (permalink / raw)


In article <4jlj79$h1k@Nntp1.mcs.net>, Mike Young <mikey@mcs.com> wrote:
>I'd been bemoaning the lack of iostreams in Ada. Would you elaborate (no pun 
>intended) more on how "&" and 'Image would be used?

Well, I'm not sure exactly what you're asking.  To print stuff out, you
need some procedure that prints text strings, and you need some way to
convert whatever you want to print out into a text string.  In Ada,
'Image is used to convert simple data types like Integer and Float into
human-readable Strings.  For complicated data types, you write your own
Image function.  The "&" operator just concatenates Strings.  So to
print out some stuff, you write:

    Put("X = " & Integer'Image(X) & "; and List = " & Image(List));

Where List is a variable of some complicated data structure.  This
converts X and List to type String, concatenates the whole mess
together, and prints it to standard output.

Does that answer your question?

>Yup. Good practice is not limited by choice of language, I see. :)

:-)

>The other sad part of function-like macros is safety of side-effects in macro 
>arguments. ...

Indeed.

>In spite of this, macros are still the only way to perform some useful tasks. 
>For example:
>
>#ifndef _DEBUG
>#define assert(a)
>#else
>#define assert(a) {if (!a) {_assert_fail(__FILE__, __LINE__, #a);}}
>#endif
>
>How would you do this in Ada?

I don't know of any way to do that in Ada.  However, that's not a huge
problem.  You indicate a run-time error by raising an exception.  You
can write an Assert procedure that checks the condition, and raises an
exception if False.  Any reasonable debugger can tell you what line of
code failed, and show you the line of code.  And any reasonable debugger
can tell the difference between an exception that is handled, with
intent to continue executing, and an exception that is unhandled, and is
therefore just a bug.

- Bob




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

* Re: some questions re. Ada/GNAT from a C++/GCC user
  1996-04-01  0:00       ` Norman H. Cohen
@ 1996-04-01  0:00         ` Robert A Duff
  1996-04-01  0:00           ` Mike Young
  1996-04-01  0:00         ` Mike Young
  1 sibling, 1 reply; 80+ messages in thread
From: Robert A Duff @ 1996-04-01  0:00 UTC (permalink / raw)


In article <4jp17p$17vn@watnews1.watson.ibm.com>,
Norman H. Cohen <ncohen@watson.ibm.com> wrote:
>Jean Ichbiah correctly saw the chaos that could result from approach (b).

I agree.

>He chose approach (c) for an exception occuring while the child task was
>still elaborating the declarative part of its task body and (a) for an
>exception occuring once the child task entered the sequence of statements
>of its task body.

IMHO, he should have chosen approach (c) for all exceptions.  A master
awaits its dependent tasks before continuing.  This would be the perfect
place to raise Tasking_Error if the dependent task(s) raise unhandled
exceptions.  Instead, the dependents silently disappear, like Ms. Rigby,
and then the parent task continues merrily on its way.

>...  (Anyone who has debugged a
>program in which a task raised an unhandled exception has quickly learned
>the value of adding a handler such as
>
>   when others =>
>      Display_Error_Message
>         ("Task of type XYZ abandoned due to unhandled exception.");
>
>to each task body.)

Yes.  Debugging these things is a nightmare, otherwise, because the
problem is not localized.

- Bob




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

* Re: some questions re. Ada/GNAT from a C++/GCC user
  1996-04-01  0:00         ` Richard A. O'Keefe
@ 1996-04-01  0:00           ` Robert A Duff
  0 siblings, 0 replies; 80+ messages in thread
From: Robert A Duff @ 1996-04-01  0:00 UTC (permalink / raw)


In article <4jof0i$2u8@goanna.cs.rmit.edu.au>,
Richard A. O'Keefe <ok@goanna.cs.rmit.edu.au> wrote:
>I am on record as being a fan of Algol 68, which is where C++ got the idea
>of mixing declarations and statements.  However, it is quite false to say
>that Ada requires 'declare', 'begin' and 'end' "for no good reason".
>
>In C++, declarations and statements _can_ look very similar.

The solution, IMHO, is to make the syntax of declarations different from
the syntax of statements.  Then, you can tell the difference, without
the extra cue of "above lie declarations, below lie statements".  In
fact, in Ada, declarations look different enough for my taste.  I can
tell them apart pretty easily, without any begin/end cues.  Besides,
what's the big deal?  If you mistake one for the other, what bugs does
that cause?

>...  See section
>6.8 of Eliis & Stroustrup, which says "to disambiguate, the whole
>statement may have to be examined to determine if it is an 
>expression-statement of a declaration".

A syntactic problem that C++ inherits from C.

>This creates a problem for people trying to read long listings:  when they
>want to find the declaration of a local identifier, it can be hard to see
>which lines are declarations, let alone which is the right declaration.
>
>C++ programmers deal with this a simple way:  they avoid long functions.
>
>Ada programmers do this too, but Ada helps with two things:

Small functions are a good idea.  But that doesn't imply that the
"right" place to split a piece of code out into a separate function is
exactly where I happen to want a very-local variable.  I don't want to
clutter the namespace with function names that have no conceptual use,
but are merely there because the language forced a function call at that
point.  After all, we don't require that every loop body contain exactly
one function call.

>(1) For variables and constants, the identifier being declared is the first
>    thing in the line.  (Ada allows, but does not require, several identifiers
>    in one declaration.  I try to avoid this.)
>
>(2) The declarations are located in specially marked regions of code.
>    Repeat
>	look up to outer 'procedure', 'function', 'declare', 'package'
>	scan down to 'begin' looking for identifier
>    until found.

Yeah, or you could use "grep".  The above algorithm doesn't work too
well if the declaration you're looking for is in another library
package.

>Of _course_ this is heavy-weight for tiny examples, but tiny examples don't
>benefit much from intermingled declarations either.

I suppose.  But you could use the same argument against while loops and
if statements.  After all, if you keep your procedures small enough,
then an algorithm written entirely with goto statements isn't so hard to
understand.

- Bob




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

* Re: some questions re. Ada/GNAT from a C++/GCC user
  1996-04-01  0:00             ` Ken Garlington
@ 1996-04-01  0:00               ` Robert A Duff
  1996-04-02  0:00                 ` Tucker Taft
  1996-04-02  0:00                 ` Ken Garlington
  0 siblings, 2 replies; 80+ messages in thread
From: Robert A Duff @ 1996-04-01  0:00 UTC (permalink / raw)


In article <315FCD11.D7E@lfwc.lockheed.com>,
Ken Garlington  <garlingtonke@lfwc.lockheed.com> wrote:
>Robert A Duff wrote:
>> 
>> I really do think the extra
>> verbosity damages readability, and there's "no good reason" for it.
>
>I do think "declare" is useful, if you name the declare block. 

OK, no argument here.  But what about the case where I don't *want* to
name it?  Adding lots of names doesn't make the code more readable.  You
only want to add names when there's something conceptually name-able.
Locally nested scopes with names seem useful, but locally nested scopes
without names, and without extra syntax, seem useful, too.

>...This gives you
>an easy way to find the end of the scope, even in the presence of nested scopes.
>Of course, an alternative would be to have the ability to name all constructs that 
>would introduce a scope. Something like?
>
>a_scope: if foo > bar then
>...
>end if a_scope;
>
>You could do this with comments, of course, but then you lose the compiler 
>checking...

True.  But don't forget that the compiler is just checking that the
names match up, which is useful, but it's not checking that the names
actually tell the truth about what "a_scope" is all about.  So let's not
attach too much significance to this amazing magic of compile-time
checking.

Note that the block_statement *name* is option in Ada, which I think is
good -- you can name it if there's a name the gives useful information,
but if the name is just "The_Loop_Body" or "The_Else_Part", you can omit
it.

- Bob




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

* Re: Iterators (was Re: some questions re. Ada/GNAT from a C++/GCC user)
  1996-03-29  0:00 ` Robert A Duff
                     ` (2 preceding siblings ...)
       [not found]   ` <4jlj79$h1k@Nntp1.mcs.net>
@ 1996-04-01  0:00   ` Robert I. Eachus
  1996-04-04  0:00   ` some questions re. Ada/GNAT from a C++/GCC user Jon S Anthony
  4 siblings, 0 replies; 80+ messages in thread
From: Robert I. Eachus @ 1996-04-01  0:00 UTC (permalink / raw)



    In article <EACHUS.96Mar29191146@spectre.mitre.org>, I said:

   > The choice was between Ada and a shell script...I chose Ada.

  In article <4jlmn5$h1k@Nntp1.mcs.net> mikey@mcs.com (Mike Young) writes:

   > I don't know about that, Robert. The equivalent shell script would be...

    No, that would NOT be the equivalent shell script.  Yes, that is
similar to the approach I would have taken with a shell script, but
the main difference is that the shell script would invoke a program
once for each file, while the Ada approach allowed me to invoke a
subprogram WITHIN a program once for each file name.

    Now the actual tasks the tools were doing were such that having a
single program run 1000 times with state passed through (other) files
would have resulted in O(N squared) preformance as the state files to
be read in each time got larger and larger.  Another path I considered
was to use a script to catenate all files together--in a pipe, not a
file--then write a separate program to take the pipe as input.  In
that case I lose the advantages of having the file name as "out of
band" information.  I'd have to prepend a structured comment to each
file before piping it, and on the other end, look for the structured
comments and process them.

    Also, I could have used a DBMS, but that would have required
different DDL and SQL for each of several tools, where with the all
Ada approach much of the code could be shared.

    So after doing a quick analysis, writing a file iterator--which
took about two hours to write and debug--was by far the best solution.
I posted the code because others may find it useful as well, even in
cases where the "simple" shell script would work.
--

					Robert I. Eachus

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




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

* Re: some questions re. Ada/GNAT from a C++/GCC user
  1996-04-01  0:00           ` Robert A Duff
  1996-04-01  0:00             ` Ken Garlington
@ 1996-04-01  0:00             ` Norman H. Cohen
  1996-04-01  0:00             ` AdaWorks
  2 siblings, 0 replies; 80+ messages in thread
From: Norman H. Cohen @ 1996-04-01  0:00 UTC (permalink / raw)


In article <Dp6y67.MI9@world.std.com>, bobduff@world.std.com
(Robert A Duff) writes: 

|>             Norm Cohen is not some evil doer ...

Well, that's always nice to hear, but I'd feel better about if Bob's post
hadn't been dated April 1. ;-)

--
Norman H. Cohen    ncohen@watson.ibm.com
--
If I write a verse
About comets, do not call
My haiku tacky.




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

* Re: some questions re. Ada/GNAT from a C++/GCC user
  1996-03-31  0:00         ` John G. Volan
  1996-03-31  0:00           ` Mike Young
  1996-04-01  0:00           ` Robert A Duff
@ 1996-04-01  0:00           ` Bruce.Conroy
  2 siblings, 0 replies; 80+ messages in thread
From: Bruce.Conroy @ 1996-04-01  0:00 UTC (permalink / raw)


In <4jmuj5$lkh@dayuc.dayton.saic.com>, John G. Volan <John_Volan@ccmail.dayton.saic.com> writes:
..

>Okay, maybe the tasking issue was a bit of a stretch.  With respect to
>tasks, I still feel that it's important to be able to distinguish the
>difference between elaboration and execution, but I agree that this
>doesn't give us a _comprehensive_ rationale for for Ada's strict rules
>separating declarations from statements.
>
>Okay, then, can anybody who was in on the Ada83/Ada95 design process
>give us a more comprehensive rationale for this strict separation, one
>consequence of which being the need for declare statements?  Warning: It
>may not be sufficient to say that we need the blocks in order to define
>the scope of the declarations, since as I pointed out, it might be
>possible to come up with a coherent definition of the scope of a
>declaration even with C's scheme (C manages it somehow, after all).

[snip]

The C rule is the same as the Ada rule. In C, any place a
statement is allowed, you can put a curly bracket followed by any number
of declarations, followed by any number of statements, followed by a
curly bracket. The only difference in Ada is that the curly brackets are
replaced by DECLARE and END, and the word BEGIN separates the
declarations from the statements. There is no semantic difference at
all. (It is C++ that allows declarations anywhere)

   Bruce Conroy





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

* Re: some questions re. Ada/GNAT from a C++/GCC user
  1996-04-01  0:00           ` Robert A Duff
  1996-04-01  0:00             ` Ken Garlington
  1996-04-01  0:00             ` Norman H. Cohen
@ 1996-04-01  0:00             ` AdaWorks
  1996-04-01  0:00               ` Mike Young
  2 siblings, 1 reply; 80+ messages in thread
From: AdaWorks @ 1996-04-01  0:00 UTC (permalink / raw)


Robert A Duff (bobduff@world.std.com) wrote:
: In article <adaworksDp5Ct8.1wt@netcom.com>,
: AdaWorks <adaworks@netcom.com> wrote:
: >  Surely you inadvertantly omitted your smiley for the phrase, "for no
: >  good reason."

: No, I'm absolutely serious.  No joke.  I really do think the extra
: verbosity damages readability, and there's "no good reason" for it.

  HmmmmMMMMMMMMmmmm.  I thought in improved readability in large programs.
  Maybe it is just a matter of what one gets used to reading.

: Don't get me wrong -- I don't think is really a big deal.

  Agreed, if we are talking only about readability. Furthermore, I
  would guess that this is a non-problem in small programs in any
  language.  I used to find it quite useful to declare in-line variables
  in BASIC programs back in the old time-sharing days.  This programming
  style fits quite well with the pre-OO model.

: Another point: The extra verbosity also encourages people to declare
: things up at the top of the procedure, rather than at the innermost
: statement list in which they're used.  

  Yes, this is usually my preference. In fact, I am not a big advocate
  of declare blocks, whether explicitly denoted (as in Ada) or implicitly
  as in C++.  My reasons are primarily related to the difficulties in
  maintaining large-scale programs.  

  What I see happening with locally declared variables is the practice
  of nesting yet another declare block inside one that already exits
  during maintenance.  After while, the nested declare blocks begin to
  take over the design.  It really annoys me when I pick up someone's
  program and find nested declare blocks several pages long.  Imagine 
  what this would like in C++. 

  This is not to say that such declarations are never useful. They are, 
  especially when they contain only a small set of declarations and
  well-bounded behavior.  One of my favorite uses of declare blocks is
  for local renaming of deeply nested record components.  

: For example, I believe I saw a
: post from Norman Cohen a while back, saying that (in some particular
: example), the extra verbosity of declare/begin/end just isn't worth it.

  The word "verbosity" feels slightly pejorative in this discussion. 
  I sometimes like the brevity of C and C++ contructs, just as I continue
  to appreciate the simplicity of BASIC. However, for large-scale systems 
  that must be maintained over a long time-span, the more explicit wording
  required by Ada seems more appropriate. 

: So here's a case where trying to FORCE people to write good code
: backfires.  Norm Cohen is not some evil doer who revels in terse, arcane
: notations!
  
  Whew! Am I ever glad to hear that.  

  Richard Riehle
  adaworks@netcom.com

-- 

richard@adaworks.com
AdaWorks Software Engineering
Suite 27
2555 Park Boulevard
Palo Alto, CA 94306
(415) 328-1815
FAX  328-1112




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

* Re: some questions re. Ada/GNAT from a C++/GCC user
  1996-04-01  0:00             ` AdaWorks
@ 1996-04-01  0:00               ` Mike Young
  1996-04-02  0:00                 ` AdaWorks
  1996-04-02  0:00                 ` Robert Dewar
  0 siblings, 2 replies; 80+ messages in thread
From: Mike Young @ 1996-04-01  0:00 UTC (permalink / raw)


AdaWorks wrote:
> 
>   What I see happening with locally declared variables is the practice
>   of nesting yet another declare block inside one that already exits
>   during maintenance.  After while, the nested declare blocks begin to
>   take over the design.  It really annoys me when I pick up someone's
>   program and find nested declare blocks several pages long.  Imagine
>   what this would like in C++.

========
Tut tut, Richard. Save the cheap shots for when they're absolutely 
necessary.

It's rare to find even poorly written C++ code "several pages long," let 
alone nested scopes that size. You can find poor programmers in any 
discipline, apparently.

>   The word "verbosity" feels slightly pejorative in this discussion.
>   I sometimes like the brevity of C and C++ contructs, just as I continue
>   to appreciate the simplicity of BASIC. However, for large-scale systems
>   that must be maintained over a long time-span, the more explicit wording
>   required by Ada seems more appropriate.

======
I may be going out on a limb here [comma] but per chance an extreme example 
might make the point [period] I don[tick]t believe Robert[tick]s point was 
in regards to notation [comma] but rather the extremes some might go to 
avoid verbosity [period] [open paren] Personally [comma] I prefer a terser 
notation where little chance of confusion exists [period] Yes [comma] 
I[tick]m aware that this may come across as somewhat patronizing 
[period][close paren]

> 
> : So here's a case where trying to FORCE people to write good code
> : backfires.

=======
Out on a limb even further, I'm rather struck by how strongly Ada attempts 
to enforce a certain set of coding standards. It took two readings of 
Barne's book to pluck out the grammar from the syntactic rules.

Mike.




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

* Re: some questions re. Ada/GNAT from a C++/GCC user
  1996-04-01  0:00       ` Norman H. Cohen
  1996-04-01  0:00         ` Robert A Duff
@ 1996-04-01  0:00         ` Mike Young
  1996-04-02  0:00           ` David Shochat
                             ` (2 more replies)
  1 sibling, 3 replies; 80+ messages in thread
From: Mike Young @ 1996-04-01  0:00 UTC (permalink / raw)


Norman H. Cohen wrote:
> immediately.  Program_Error results if the task is activated before its
> task body has been elaborated, so the fact that declarations in a
> declarative part are elaborated in order is sufficient to guarantee that
> any declaration referred to by the task unit has already been elaborated
> by the time a task of the corresponding task type is activated.

=======
I'm confused. I thought I read an explicit rule (in Barne's book) that 
no order of evaluation was specified for declarations.

Mike.




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

* Re: some questions re. Ada/GNAT from a C++/GCC user
  1996-04-01  0:00         ` Robert A Duff
@ 1996-04-01  0:00           ` Mike Young
  1996-04-02  0:00             ` Norman H. Cohen
  1996-04-02  0:00             ` Robert A Duff
  0 siblings, 2 replies; 80+ messages in thread
From: Mike Young @ 1996-04-01  0:00 UTC (permalink / raw)


Robert A Duff wrote:
> 
> In article <4jp17p$17vn@watnews1.watson.ibm.com>,
> Norman H. Cohen <ncohen@watson.ibm.com> wrote:
> >Jean Ichbiah correctly saw the chaos that could result from approach (b).
> 
> I agree.
> 
> >He chose approach (c) for an exception occuring while the child task was
> >still elaborating the declarative part of its task body and (a) for an
> >exception occuring once the child task entered the sequence of statements
> >of its task body.
> 
> IMHO, he should have chosen approach (c) for all exceptions.  A master
> awaits its dependent tasks before continuing.  This would be the perfect
> place to raise Tasking_Error if the dependent task(s) raise unhandled
> exceptions.  Instead, the dependents silently disappear, like Ms. Rigby,
> and then the parent task continues merrily on its way.

=======
This appears somewhat frightening to me, that I might catch exceptions 
from unknown and unspecified source at any given moment. What exactly 
should I do with this exception aside from terminating? (How is this 
different from SIGCHLD?) This might have the right intuitive feel if 
this behavior applied up until some known point where parent and child 
parts ways, but what point would that be? 

Mike.




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

* Re: some questions re. Ada/GNAT from a C++/GCC user
  1996-03-30  0:00       ` Robert A Duff
  1996-03-31  0:00         ` AdaWorks
@ 1996-04-01  0:00         ` Richard A. O'Keefe
  1996-04-01  0:00           ` Robert A Duff
  1996-04-01  0:00         ` Robert Dewar
  2 siblings, 1 reply; 80+ messages in thread
From: Richard A. O'Keefe @ 1996-04-01  0:00 UTC (permalink / raw)


bobduff@world.std.com (Robert A Duff) writes:
>You miss the point.  C++ doesn't require "{" and "}" in this case -- the
>case we're talking about is where no visual cues are necessary.  I can
>understand that one might like "begin" better than "{", but this is a
>case where C++ requires nothing at all, and Ada requires "declare",
>"begin", and "end", for no good reason.

I am on record as being a fan of Algol 68, which is where C++ got the idea
of mixing declarations and statements.  However, it is quite false to say
that Ada requires 'declare', 'begin' and 'end' "for no good reason".

In C++, declarations and statements _can_ look very similar.  See section
6.8 of Eliis & Stroustrup, which says "to disambiguate, the whole
statement may have to be examined to determine if it is an 
expression-statement of a declaration".

This creates a problem for people trying to read long listings:  when they
want to find the declaration of a local identifier, it can be hard to see
which lines are declarations, let alone which is the right declaration.

C++ programmers deal with this a simple way:  they avoid long functions.

Ada programmers do this too, but Ada helps with two things:

(1) For variables and constants, the identifier being declared is the first
    thing in the line.  (Ada allows, but does not require, several identifiers
    in one declaration.  I try to avoid this.)

(2) The declarations are located in specially marked regions of code.
    Repeat
	look up to outer 'procedure', 'function', 'declare', 'package'
	scan down to 'begin' looking for identifier
    until found.

Of _course_ this is heavy-weight for tiny examples, but tiny examples don't
benefit much from intermingled declarations either.

-- 
Result of state election = 50% Victorians didn't get MP of their choice.
Richard A. O'Keefe; http://www.cs.rmit.edu.au/~ok; RMIT Comp.Sci.




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

* Re: some questions re. Ada/GNAT from a C++/GCC user
  1996-03-27  0:00 some questions re. Ada/GNAT from a C++/GCC user Bill Newman
                   ` (4 preceding siblings ...)
  1996-03-30  0:00 ` Simon Wright
@ 1996-04-01  0:00 ` Laurent Guerby
  1996-04-01  0:00   ` Robert A Duff
  5 siblings, 1 reply; 80+ messages in thread
From: Laurent Guerby @ 1996-04-01  0:00 UTC (permalink / raw)


[comp.lang.c++ removed since this has nothing to do with C++]

Robert A Duff writes
[deleted]
: Sorry to keep disagreeing with you, John, but this seems like the tail
: wagging the dog.  If the reason I'm required to add 3 extra junk lines
: of code, just to declare a simple variable, is because of tasks, then
: that's just poor language design.  
[deleted]

   If you follow  the  GNU Coding Standard,  here  is how you  have to
declare (indent) an inner block (loop, if ...) :

   {
      int local;

      foo (local);
      bar (global);
   }

    (I don't think the  blank line is  required, but I've nearly never
seen a C program that do not put a blank line between locals and code,
even in "bad" written code.) The Ada version looks like :

   declare
      Local : Integer;
   begin
      Foo (Local);
      Bar (Global);
   end;

   Hey, the  number of line  is the same !  Cool isn't it ;-). Ok, you
type more charaters.

   BTW, in any real code, the source documentation will add some lines
to explain the   use of local   (if not clear  according  to the  name
choosen) and the algorithm, making all this discussion a bit useless.

   And an Ada   declaritive region is  not  a C  one,  just play  with
aggregates and  try to translate it  in C,  it  will add a  few "junk"
lines  of code ;-). Semantic differences  between a declarative region
and a handled sequence of statement put aside.

: - Bob

-- 
--  Laurent Guerby, student at Telecom Bretagne (France), Team Ada
--  "Use the Source, Luke. The Source will be with you, always (GPL)"
--  http://www-eleves.enst-bretagne.fr/~guerby/ (GATO Project)
--  Try GNAT, the GNU Ada 95 compiler (ftp://cs.nyu.edu/pub/gnat)




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

* Re: some questions re. Ada/GNAT from a C++/GCC user
  1996-03-30  0:00       ` Robert A Duff
  1996-03-31  0:00         ` AdaWorks
  1996-04-01  0:00         ` Richard A. O'Keefe
@ 1996-04-01  0:00         ` Robert Dewar
  2 siblings, 0 replies; 80+ messages in thread
From: Robert Dewar @ 1996-04-01  0:00 UTC (permalink / raw)


Bob said:

"You miss the point.  C++ doesn't require "{" and "}" in this case -- the
case we're talking about is where no visual cues are necessary.  I can
understand that one might like "begin" better than "{", but this is a
case where C++ requires nothing at all, and Ada requires "declare",
"begin", and "end", for no good reason."

I strongly disagree. For VERY good reason! 

To me, considering declarations and statements to be the same sort of
thing is confusion, not unification :-)






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

* Re: some questions re. Ada/GNAT from a C++/GCC user
  1996-03-31  0:00         ` John G. Volan
  1996-03-31  0:00           ` Mike Young
@ 1996-04-01  0:00           ` Robert A Duff
  1996-04-03  0:00             ` Scott Leschke
  1996-04-01  0:00           ` Bruce.Conroy
  2 siblings, 1 reply; 80+ messages in thread
From: Robert A Duff @ 1996-04-01  0:00 UTC (permalink / raw)


In article <4jmuj5$lkh@dayuc.dayton.saic.com>,
John G. Volan  <John_Volan@ccmail.dayton.saic.com> wrote:
>Okay, then, can anybody who was in on the Ada83/Ada95 design process
>give us a more comprehensive rationale for this strict separation, one
>consequence of which being the need for declare statements?

I was in on the Ada 95 design process, and the reason is simple: It was
like that in Ada 83, and there was no mandate to change it.

I was not in on the Ada 83 design, but I suspect it has something to do
with the Pascal heritage.  Also, some people have said they find it
easier to read the code if they can find the declarations.  I don't
really agree with that, but I suppose it's mainly a matter of taste.

>    -- This is NOT Ada, this is CRAPOLA (C-Reminiscent Ada-like Perversion
>    -- Of Language Aspects)  :-) :

Now, now.  I see the smiley, but still, this is just an ad-hominem
attack.

>    begin
>        ...
>        if Smaller then
>            X : Integer;
>            ...
>        elsif Bigger then
>            X : Long_Integer;
>            ...
>        end if;
>        ...
>        -- is X in scope here, and if so, what the heck is it?

No, X is not in scope here.  There are two things called X, and their
scopes do not overlap.  The first X starts to exist at the beginning of
the 'then' part, and ceases to exist at the end of that statement list.

The semantics of CRAPOLA should be exactly the same as if, in Ada, you
put declare-begin-end:

        if Smaller then
            declare
                X : Integer;
            begin
                ...
            end;
        elsif Bigger then
            declare
	        X : Long_Integer;
            begin
                ...
            end;
        end if;

>        ... loop
>            Y : Integer;
>            Get (Y);
>            type A is array (1 .. Y) of Integer;
>            package P is new Generic_P (A);
>            -- do these things get elaborated & destroyed every iteration?

Yes.  And if any of the declarations need finalization, they will get
finalized at the end of each iteration.

>            ...
>        end loop ... ;
>        ...
>        -- are Y, A, and P still in scope here?

No.

>    end ... ;

>I assume that the only thing that would make sense would be to treat
>every structured statement as the moral equivalent of a begin/end.

You assume right.  That is, we're arguing about syntactic sugar; the
semantics doesn't change if you allow declare-begin-end to be left out.

- Bob




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

* Re: some questions re. Ada/GNAT from a C++/GCC user
  1996-04-01  0:00 ` Laurent Guerby
@ 1996-04-01  0:00   ` Robert A Duff
  0 siblings, 0 replies; 80+ messages in thread
From: Robert A Duff @ 1996-04-01  0:00 UTC (permalink / raw)


In article <4xn34w5i73.fsf@leibniz.enst-bretagne.fr>,
Laurent Guerby <Laurent.Guerby@enst-bretagne.fr> wrote:
>   If you follow  the  GNU Coding Standard,  here  is how you  have to
>declare (indent) an inner block (loop, if ...) :
>
>   {
>      int local;
>
>      foo (local);
>      bar (global);
>   }
>
>   declare
>      Local : Integer;
>   begin
>      Foo (Local);
>      Bar (Global);
>   end;
>
>   Hey, the  number of line  is the same !  

No, the number of lines is not the same.  In C++, the curly braces are
usually there *anyway*, because you're inside a loop or an if statement
or something.

>...Cool isn't it ;-). Ok, you
>type more charaters.

Shrug.  I don't care about the number of keystrokes -- Emacs types it
all automatically, anyway.  I was complaining that the extra verbosity
reduces readability.  Verbosity is good when it adds useful information
to the code.  It's bad when it merely clutters the code.

- Bob




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

* Re: some questions re. Ada/GNAT from a C++/GCC user
  1996-03-31  0:00         ` AdaWorks
@ 1996-04-01  0:00           ` Robert A Duff
  1996-04-01  0:00             ` Ken Garlington
                               ` (2 more replies)
  0 siblings, 3 replies; 80+ messages in thread
From: Robert A Duff @ 1996-04-01  0:00 UTC (permalink / raw)


In article <adaworksDp5Ct8.1wt@netcom.com>,
AdaWorks <adaworks@netcom.com> wrote:
>  Surely you inadvertantly omitted your smiley for the phrase, "for no
>  good reason."

No, I'm absolutely serious.  No joke.  I really do think the extra
verbosity damages readability, and there's "no good reason" for it.
Don't get me wrong -- I don't think is really a big deal.

Another point: The extra verbosity also encourages people to declare
things up at the top of the procedure, rather than at the innermost
statement list in which they're used.  For example, I believe I saw a
post from Norman Cohen a while back, saying that (in some particular
example), the extra verbosity of declare/begin/end just isn't worth it.
That's sad, because when you move the thing up to the top, you make the
declaration harder to understand:

    - Its scope is larger, so to see how it's used, you have to read
      more code.

    - In many cases, the initial value of the variable is not known at
      the top of the procedure -- it's something that gets calculated
      along the way.  So you have to initialize it with an assignment
      statement.  That's bad, because it becomes harder to understand
      whether the variable is properly initialized.

    - In many cases, making the scope bigger than it needs to be causes
      the object to be a variable, rather than a constant.  Constants
      are easier to understand than variables.

So here's a case where trying to FORCE people to write good code
backfires.  Norm Cohen is not some evil doer who revels in terse, arcane
notations!

- Bob




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

* Re: some questions re. Ada/GNAT from a C++/GCC user
  1996-03-29  0:00   ` Brian Rogoff
@ 1996-04-01  0:00     ` Mark A Biggar
  1996-04-01  0:00       ` Robert A Duff
  0 siblings, 1 reply; 80+ messages in thread
From: Mark A Biggar @ 1996-04-01  0:00 UTC (permalink / raw)


In article <ROGOFF.96Mar29163036@sccm.Stanford.EDU> rogoff@sccm.stanford.edu writes:
>   > Why doesn't Ada 95 allow declarations to be interspersed with ordinary
>   > statements as C++ does?  (Or does it?  _Ada as a Second Language_ is a
>   > big book!)  It seems to me that the C++ approach is a small but
>   > definite win.  Does it interact very badly somehow with all those
>   > guarantees on elaboration order?
>
>   To intersperse declarations, you have to use a block_statement, like
>   this:
>
>       for I in Some_String'Range loop -- I wish I could easily use an iterator here ;-)
>	   declare
>	       X: constant Character := Some_String(I);
>	   begin
>	       ...
>	   end;
>       end loop;
>
>   To me, the "declare", "begin", and "end" are just useless verbosity.
>   I prefer the C++ rule.
>
>Yes, this seems needlessly verbose. Any Ada guru have a good reason why it has 
>to be this way?

Yeah, 2 reasons.  The first goes clear back to the original Ada Strawman
documnt which calls for a clear distinction between declarations and code
statements.  The second reason is that is also the method of wraping an
exception handler around soem arbitary piece of code:

for I in Some_String'Range loop 
    declare
        X: constant Character := Some_String(I);
    begin
        ...
    exception
        when ... ;
    end;
end loop;


--
Mark Biggar
mab@wdl.loral.com





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

* Re: some questions re. Ada/GNAT from a C++/GCC user
  1996-04-02  0:00                   ` Robert A Duff
@ 1996-04-02  0:00                     ` Ken Garlington
  1996-04-02  0:00                       ` Robert A Duff
  0 siblings, 1 reply; 80+ messages in thread
From: Ken Garlington @ 1996-04-02  0:00 UTC (permalink / raw)


Robert A Duff wrote:
> 
> It doesn't seem to me that the declare block above needs a name -- it's
> not that big, and its function is intimately tied to the surrounding
> loop, rather than being stand-alone.

"Intimately tied to the surrounding loop" - that might be a useful fact
to communicate to the maintainer. How about a name for the loop, and
a name for the block that indicates _how_ it is tied to the loop?

> I have no problem with the exception keyword.  To me, an exception
> handler is much more of a "big deal" than a plain old variable
> declaration.

Well, since you wouldn't bite on "begin..."

What about a variable declaration that's not so plain? Would I still
need a block statement in that case?




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

* Re: some questions re. Ada/GNAT from a C++/GCC user
  1996-04-02  0:00                     ` Ken Garlington
@ 1996-04-02  0:00                       ` Robert A Duff
  1996-04-03  0:00                         ` Ken Garlington
  1996-04-03  0:00                         ` David Emery
  0 siblings, 2 replies; 80+ messages in thread
From: Robert A Duff @ 1996-04-02  0:00 UTC (permalink / raw)


In article <316120DC.1F93@lfwc.lockheed.com>,
Ken Garlington  <garlingtonke@lfwc.lockheed.com> wrote:
>"Intimately tied to the surrounding loop" - that might be a useful fact
>to communicate to the maintainer. How about a name for the loop, and
>a name for the block that indicates _how_ it is tied to the loop?

No need to name the loop -- that's pretty much the only thing in that
function, so the function name serves the purpose.  Name for the loop
body?  Maybe, but I don't see how the fact that I *happened* to want a
very-local variable has anything to do with it.

>Well, since you wouldn't bite on "begin..."

:-)

>What about a variable declaration that's not so plain? Would I still
>need a block statement in that case?

I think you should be able to introduce a named block wherever you like.

- Bob




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

* Re: some questions re. Ada/GNAT from a C++/GCC user
  1996-03-30  0:00     ` Richard Pitre
  1996-03-30  0:00       ` Robert A Duff
@ 1996-04-02  0:00       ` Robert I. Eachus
  1 sibling, 0 replies; 80+ messages in thread
From: Robert I. Eachus @ 1996-04-02  0:00 UTC (permalink / raw)


In article <Dp6y67.MI9@world.std.com> bobduff@world.std.com (Robert A Duff) writes:

  > No, I'm absolutely serious.  No joke.  I really do think the extra
  > verbosity damages readability, and there's "no good reason" for it.
  > Don't get me wrong -- I don't think is really a big deal.
  
   There is "good reason" for it, and it was well thought out.
However, this discussion has tended to focus on the exceptions,
rather than the rule.

   When Ada 83 was being standardized, one quick measure of how good
your programming style was was to measure the ratio of statements to
subprogram declarations.  Anything over 20 was bad, and I think the
original ROLM Ada compiler weighed in at nine.

   Given this and that the "innermost enclosing scope" for
significantly more than fifty percent of the source was expected to be
a declarative part, not a sequence of statements, it made sense to
collect all of the (rare) statements in one place.  The only
declarations allowed to be mixed with statements were the implicit
declarations of statement labels, loop and block names, and loop
parameters.  (Quick, when was the last time you used a block name in
Ada?  For that matter when was the last time you used a statement
label?  I've only used two in the last fifteen years, outside of
compiler tests.)

  The Ada 83 rule which was a royal pain however, was the one that
split a declarative part into _basic_declarative_items_ and
_later_declarative_items_.  Thankfully this is gone in Ada 95.  Ever
hoist a type or variable declaration out of a body in a package body
to share it with another subprogram, and discover you have to move it
back five pages?

  > Another point: The extra verbosity also encourages people to declare
  > things up at the top of the procedure, rather than at the innermost
  > statement list in which they're used.  For example, I believe I saw a
  > post from Norman Cohen a while back, saying that (in some particular
  > example), the extra verbosity of declare/begin/end just isn't worth it.
  > That's sad, because when you move the thing up to the top, you make the
  > declaration harder to understand:

  >    - Its scope is larger, so to see how it's used, you have to read
  >	 more code.
 
  See above.  I've even used nested packages to get objects more
closely associated with the related subprograms or tasks.

  >    - In many cases, the initial value of the variable is not known at
  >	 the top of the procedure -- it's something that gets calculated
  >	 along the way.  So you have to initialize it with an assignment
  >	 statement.  That's bad, because it becomes harder to understand
  >	 whether the variable is properly initialized.

  If the initial value really isn't available at the top of the
subprogram, I will consider a separate subprogram or a declare block.
Usually it does indicate a (often minor) flaw in my design.

  >    - In many cases, making the scope bigger than it needs to be causes
  >	 the object to be a variable, rather than a constant.  Constants
  >	 are easier to understand than variables.

  I've never seen this.  Much more common in my experience is the
issue of how closely nested a variable should be.  There are times
when proper design hoists a constant to an enclosing scope, where the
constant is a variable.  When I run across this, I usually pass the
variable as a formal in parameter so that it is a constant in the
inner scope.  Unfortunately, this allows aliasing to occur, unless you
can order the declarations so that the variable is not in scope of the
inner body. ;-( Fortunately with the fix mentioned above, I can fix
this in Ada 95--it just occaisionally looks a little strange in the
code:

  package body Foo is
    ...
    procedure Bar (X: in Count; Y...) is ...end Bar;
    ...
    X: Count := 0; 
    ...
    procedure Foob is
    ...
    begin
      Bar(X,Z);
      ...
    end Foob;
    ...
  end Foo;

  > So here's a case where trying to FORCE people to write good code
  > backfires.  Norm Cohen is not some evil doer who revels in terse,
  > arcane notations!

   I agree, but I have yet to see even a minor problem.  The latter
declarative part thing was much more serious, and still not a serious
problem.

--

					Robert I. Eachus

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




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

* Re: some questions re. Ada/GNAT from a C++/GCC user
  1996-04-01  0:00         ` Mike Young
@ 1996-04-02  0:00           ` David Shochat
  1996-04-02  0:00             ` Mike Young
  1996-04-02  0:00           ` Norman H. Cohen
  1996-04-02  0:00           ` Robert Dewar
  2 siblings, 1 reply; 80+ messages in thread
From: David Shochat @ 1996-04-02  0:00 UTC (permalink / raw)



In article <3160B03B.5230@mcs.com> Mike Young <mikey@mcs.com> writes:

    Mike> ======= I'm confused. I thought I read an explicit rule (in
    Mike> Barne's book) that no order of evaluation was specified for
    Mike> declarations.

RM95 3.11/7:
"The elaboration of a declarative_part consists of the elaboration of 
the declarative_items, if any, in the order in which they are given 
in the declarative_part."

Maybe you were thinking of the actual parameters in a subprogram call 
(6.4/10).
-- 
David Shochat
dshochat@logicon.com  shochat@roosevelt.logicon.com




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

* Re: some questions re. Ada/GNAT from a C++/GCC user
  1996-04-02  0:00                 ` Tucker Taft
@ 1996-04-02  0:00                   ` Felaco
  1996-04-02  0:00                     ` Robert Dewar
  1996-04-03  0:00                     ` Mark A Biggar
  0 siblings, 2 replies; 80+ messages in thread
From: Felaco @ 1996-04-02  0:00 UTC (permalink / raw)


Tucker Taft (stt@henning.camb.inmet.com) wrote:

: I find the easiest way to deal with extra, seemingly unnecessary,
: levels of constructs is to "half" indent.

[snip]

: Another alternative is to "merge" constructs, e.g.:

:     if A > 0 then declare
:         B : Integer := A;
:     begin
:         -- This is my normal indent of 4
:         A := B * A;
:     end; else
:         A := 2;
:     end if;

I prefer this approach, but tell that to Rational, whose Apex product 
insists on changing it on me...

Anyway, it would have been *nice* if we could dispense with the 'declare' 
and the 'end' when a new scope is introduced anyway, like in an if block 
or a loop.  I don't see how backwards compatibility with Ada83 would be 
compromised, or any other rules would have to change.

ie:

   if whatever then
       X : Integer;
       Y : Some_Task_Type;
     begin
       do_something;
   else
       loop
           Y : Boolean;
         begin
           do_something_else;
           exit when Y;
       end loop;
   end if;

I find the above just as human-readable, and still machine-readable.  The 
presence of the declaration signals that a new scope is introduced, the 
begin serves as a separator and the else serves as the end.  Why not?

: In any case, if our biggest problem with Ada 95 is that it requires 
: an explicit "declare" to introduce very local variables, I'm content...

I doubt it's the biggest problem with Ada 95, but it is one of the first 
things that narrow-minded people who resent being told to learn new 
languages pick up on and complain about.  Sometimes you need to make some 
concessions to these people whether you like it or not.

-------------------------------------------------------------------------------
Chris Felaco                               Phone: x4631 (Raynet 444, Local 842)
Raytheon Company                                         Email: bcf@ssd.ray.com
-------------------------------------------------------------------------------




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

* Re: some questions re. Ada/GNAT from a C++/GCC user
  1996-04-02  0:00           ` David Shochat
@ 1996-04-02  0:00             ` Mike Young
  0 siblings, 0 replies; 80+ messages in thread
From: Mike Young @ 1996-04-02  0:00 UTC (permalink / raw)


David Shochat wrote:
> 
> RM95 3.11/7:
> "The elaboration of a declarative_part consists of the elaboration of
> the declarative_items, if any, in the order in which they are given
> in the declarative_part."
> 
> Maybe you were thinking of the actual parameters in a subprogram call
> (6.4/10).
> --

=========
Thank you all for the copious correction. I confused it with the Eiffel 
books that I am currently plowing through in parallel with Ada. A Java 
bigot in the next office started a local renaissance of sorts in regards 
to OO languages. I'm finding many things to like in Ada (tasking tops 
the list), but still looking for compelling reason...

Mike.




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

* Re: some questions re. Ada/GNAT from a C++/GCC user
  1996-04-02  0:00                   ` Felaco
@ 1996-04-02  0:00                     ` Robert Dewar
  1996-04-03  0:00                     ` Mark A Biggar
  1 sibling, 0 replies; 80+ messages in thread
From: Robert Dewar @ 1996-04-02  0:00 UTC (permalink / raw)


Chris said

"I doubt it's the biggest problem with Ada 95, but it is one of the first
things that narrow-minded people who resent being told to learn new
languages pick up on and complain about.  Sometimes you need to make some
concessions to these people whether you like it or not."

If we took all the features that someone or other claims are "one of the
first things that .. [people complain about]", we would have a list
long enough to fill a book the size of the Ada standard.

Almost always this sort of thing is anecdotal, and I must say I react
with skepticism, especially when the issue is a trivial one like this.






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

* Re: some questions re. Ada/GNAT from a C++/GCC user
  1996-04-01  0:00               ` Mike Young
@ 1996-04-02  0:00                 ` AdaWorks
  1996-04-02  0:00                 ` Robert Dewar
  1 sibling, 0 replies; 80+ messages in thread
From: AdaWorks @ 1996-04-02  0:00 UTC (permalink / raw)


Mike Young (mikey@mcs.com) wrote:
: Richard Riehle AdaWorks wrote:
: > 
: >   It really annoys me when I pick up someone's
: >   program and find nested declare blocks several pages long.  Imagine
: >   what this would like in C++.

: ========
: Tut tut, Richard. Save the cheap shots for when they're absolutely 
: necessary.

  Agreed. My gratuitous reference to C++ is inappropriate.  My point,
  obfuscated by my "cheap shot," was that I am not a proponent of
  ad hoc variables whether they are disguised within "declare ... end"
  statements are drift freely through the code like little pieces of
  floatsom.  On the other hand I realize that this is often an important
  and useful feature of a programming language even though it is easily 
  abused.  

: It's rare to find even poorly written C++ code "several pages long," let 
: alone nested scopes that size. You can find poor programmers in any 
: discipline, apparently.

  HmmmMMMMmmmm.  Perhaps a case could be made that the Ada declare
  block actually encourages bad coding style because of its easy
  readability.  Whereas other languages provide a greater incentive
  for programmers to be economical in their use of this feature
  because of the potential for misinterpretation. 

: =======
: Out on a limb even further, I'm rather struck by how strongly Ada attempts 
: to enforce a certain set of coding standards. It took two readings of 
: Barne's book to pluck out the grammar from the syntactic rules.

  Actually, the primary management-level issues in Ada are threefold:

      1) The compiler should catch the maximum number of errors 
         possible through every stage of the software lifecycle,
 
      2) The run-time executive should catch the remaining errors in a way 
         that can be handled via source code statements,

      3) A deployed Ada program should be extensible, and maintainable
         with minimal risk to the existing design.

  Nearly everything in the Ada language is designed to satisfy these
  goals throughout the full life-cycle of an Ada program. Supporting
  ideas such as readability, understandability, OOP, etc., are simply
  mechanisms for implementing those goals.

  I realize that some readers will have alternate viewpoints or even 
  refinements, or supplemental goals vis a vis my  rather bold declaration.  
  So be it.   
 
  Not all languages are designed to satisfy the same management goals.

  Richard Riehle
  adaworks@netcom.com
-- 

richard@adaworks.com
AdaWorks Software Engineering
Suite 27
2555 Park Boulevard
Palo Alto, CA 94306
(415) 328-1815
FAX  328-1112




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

* Re: some questions re. Ada/GNAT from a C++/GCC user
  1996-03-31  0:00           ` Mike Young
@ 1996-04-02  0:00             ` Glenn H. Porter
  1996-04-02  0:00               ` Jonas Nygren
                                 ` (2 more replies)
  0 siblings, 3 replies; 80+ messages in thread
From: Glenn H. Porter @ 1996-04-02  0:00 UTC (permalink / raw)


Most folks that responded to this missed something.  That it is simply
easier to parse source code that has all of the declarations in one
section while building a symbol table.  They're all there in a group,
bundled together like in Pascal, making it much simpler to identify
them and enter them in the symbol table.

Glenn





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

* Re: some questions re. Ada/GNAT from a C++/GCC user
  1996-04-02  0:00             ` Glenn H. Porter
@ 1996-04-02  0:00               ` Jonas Nygren
  1996-04-02  0:00               ` Robert Dewar
  1996-04-03  0:00               ` Geert Bosch
  2 siblings, 0 replies; 80+ messages in thread
From: Jonas Nygren @ 1996-04-02  0:00 UTC (permalink / raw)


Glenn H. Porter wrote:
> 
> Most folks that responded to this missed something.  That it is simply
> easier to parse source code that has all of the declarations in one
> section while building a symbol table.  They're all there in a group,
> bundled together like in Pascal, making it much simpler to identify
> them and enter them in the symbol table.
> 
> Glenn

I have a PentiumPro 150 on my desk and I believe it could handle that
extra load :-).

-------------------------------------------------------
--    Jonas Nygren
--    ehsjony@ehs.ericsson.se
-------------------------------------------------------




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

* Re: some questions re. Ada/GNAT from a C++/GCC user
  1996-04-01  0:00               ` Mike Young
  1996-04-02  0:00                 ` AdaWorks
@ 1996-04-02  0:00                 ` Robert Dewar
  1 sibling, 0 replies; 80+ messages in thread
From: Robert Dewar @ 1996-04-02  0:00 UTC (permalink / raw)


Mike says:

"It's rare to find even poorly written C++ code "several pages long," let
alone nested scopes that size. You can find poor programmers in any
discipline, apparently."

Odd! Many people say this sort of thing about both C++ and Ada, but in
my actual experience, real life large scale C++ and Ada programs do not
conform to this dictum. Very long files and procedures are common.

Sometimes, this is inevitable. Both Ada and C++ provide no way of separately
compiling pieces of a large switch/case statement. So if your application
has a case statement with hundreds of branches, then you are going to have
a long procedure, even if you write externs and calls for procedures for
each branch. 

Sometimes, it is true that long = bad code, but this is by no means always
the case. I have often seen short bad code, and good long code, the
correlation is only vague. What makes code easy to read is not shortness
per se, but good structure and organization, and logical thinking. 

I am actually surprised how often we run into the case of very large
single procedures, but it happens in both the Ada and C++ code I have
seen (you really would not expect much difference between the two
languages in this particular respect!)





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

* Re: some questions re. Ada/GNAT from a C++/GCC user
  1996-04-02  0:00             ` Glenn H. Porter
  1996-04-02  0:00               ` Jonas Nygren
@ 1996-04-02  0:00               ` Robert Dewar
  1996-04-03  0:00               ` Geert Bosch
  2 siblings, 0 replies; 80+ messages in thread
From: Robert Dewar @ 1996-04-02  0:00 UTC (permalink / raw)


Glenn said:

>Most folks that responded to this missed something.  That it is simply
>easier to parse source code that has all of the declarations in one
>section while building a symbol table.  They're all there in a group,
>bundled together like in Pascal, making it much simpler to identify
>them and enter them in the symbol table.

That's simply a misconception. For correct programs, there is obviously
no difference in the two cases from a compiler's point of view.

For error recovery, the Pascal/Ada conventions are harder work for the
compiler, because it has to try hard to identify the proper breakpoint
between declarations and statements in mangled programs. If it gets
confused on this point, you can get a lot of junk cascaded errors.

If you have not written a compiler, you often find that your intuitions
as to what might or might not help a compiler implementation are quite
wrong. Even if you DO know a lot about compilers, you can find yourself
surprised. There were restrictions in Ada 83 that were supposed to make
life easier for the compier, but had exactly the opposite effect (e.g.,
for most compilers, the restrictoin on subunit names, now removed in
Ada 95).





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

* Re: some questions re. Ada/GNAT from a C++/GCC user
  1996-04-01  0:00         ` Mike Young
  1996-04-02  0:00           ` David Shochat
  1996-04-02  0:00           ` Norman H. Cohen
@ 1996-04-02  0:00           ` Robert Dewar
  2 siblings, 0 replies; 80+ messages in thread
From: Robert Dewar @ 1996-04-02  0:00 UTC (permalink / raw)


Mike said

"I'm confused. I thought I read an explicit rule (in Barne's book) that
no order of evaluation was specified for declarations."

You are *indeed* seriously confused. Declarations in Ada are elaborated
strictly in order, and John Barnes certainly did not say anything that
corresponds to your memory.

In fact the serial elaboration of declarations (conciously copied by the
way from Algol-68) is one of the key design points in Ada.

Note of course that whenever we say something is done strictly in order,
we are talking about the formal semantic description, excluding RM 11.6.
There are two respects in which this description may not correspond to
the generated code.

1. THe compiiler is always allowed to shuffle things around if no Ada
	program can see any effect (using the formally defned meaning
	of external effect) of the change.

2. 11.6 allows certain chnges in the semantics of raising of
	predefined exceptions. of course this is relevant only if
	a declaration raises an exception.





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

* Re: some questions re. Ada/GNAT from a C++/GCC user
  1996-04-01  0:00               ` Robert A Duff
@ 1996-04-02  0:00                 ` Tucker Taft
  1996-04-02  0:00                   ` Felaco
  1996-04-02  0:00                 ` Ken Garlington
  1 sibling, 1 reply; 80+ messages in thread
From: Tucker Taft @ 1996-04-02  0:00 UTC (permalink / raw)


Bob Duff and Norm Cohen wrote:

[lot's of reasonable stuff about good and bad verbosity]

Be that as it may...

I find the easiest way to deal with extra, seemingly unnecessary,
levels of constructs is to "half" indent.

E.g.:

    if A > 0 then
      declare
        B : Integer := A;
      begin
        -- This is my normal indent of 4
        A := B * A;
      end;
    else
        A := 2;
    end if;

Another alternative is to "merge" constructs, e.g.:

    if A > 0 then declare
        B : Integer := A;
    begin
        -- This is my normal indent of 4
        A := B * A;
    end; else
        A := 2;
    end if;

Neither of these win the beauty award, but somehow not having to
indent so far makes the extra level of construct sufficiently
less painful that the pain doesn't overwhelm the gain.

In any case, if our biggest problem with Ada 95 is that it requires 
an explicit "declare" to introduce very local variables, I'm content...

-Tucker Taft   stt@inmet.com   http://www.inmet.com/~stt/
Intermetrics, Inc.  Cambridge, MA  USA




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

* Re: some questions re. Ada/GNAT from a C++/GCC user
  1996-04-01  0:00           ` Mike Young
  1996-04-02  0:00             ` Norman H. Cohen
@ 1996-04-02  0:00             ` Robert A Duff
  1 sibling, 0 replies; 80+ messages in thread
From: Robert A Duff @ 1996-04-02  0:00 UTC (permalink / raw)


In article <3160B2F1.260D@mcs.com>, Mike Young  <mikey@mcs.com> wrote:
>This appears somewhat frightening to me, that I might catch exceptions 
>from unknown and unspecified source at any given moment. 

No, it's not that bad.  The parent and child are synchronized at a known
point.  In any case, it's no worse than the current situation, which is
that the exception is simply ignored, and the task silently disappears.
At some later time ("any given moment!") some other task will probably
try to communicate with the dead task, and this will cause *another*
exception, or else a deadlock, depending on exactly how the
communication is done.

>...What exactly 
>should I do with this exception aside from terminating?

Nothing.  You would terminate, normally.  And then you would debug the
problem.  My point is just that debugging is a lot easier if you can
pinpoint the problem more closely to its true source.

This is no different from *most* cases of raising an exception -- the
normal response is to terminate the program.  Then the programmer fixes
the bug.  In *some* applications it makes sense to handle all exceptions
and try to be fault tolerant, but that's a very difficult thing to do
properly, and usually is not done.  For most exceptions in most programs
you simply want to terminate the program.

- Bob




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

* Re: some questions re. Ada/GNAT from a C++/GCC user
  1996-04-01  0:00         ` Mike Young
  1996-04-02  0:00           ` David Shochat
@ 1996-04-02  0:00           ` Norman H. Cohen
  1996-04-02  0:00           ` Robert Dewar
  2 siblings, 0 replies; 80+ messages in thread
From: Norman H. Cohen @ 1996-04-02  0:00 UTC (permalink / raw)


In article <3160B03B.5230@mcs.com>, Mike Young <mikey@mcs.com> writes: 

|> I'm confused. I thought I read an explicit rule (in Barne's book) that
|> no order of evaluation was specified for declarations.

You're confused.  The word "evaluation" applies to expressions, not
declarations.  Declarations get "elaborated", not evaluated.

It is indeed true that no evaluation order is specified for the
subexpressions of an expression (or, in most cases, such as

   A(F(X)) := B(G(X));  -- A and B are arrays, F and G are functions

for the various expressions in a statement).

However, the declarations in a sequence of declarations are always
elaborated in order.  (Barnes has an explicit statement to that effect
near the bottom of page 72 of his Ada 95 book.)

--
Norman H. Cohen    ncohen@watson.ibm.com




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

* Re: some questions re. Ada/GNAT from a C++/GCC user
  1996-04-01  0:00           ` Mike Young
@ 1996-04-02  0:00             ` Norman H. Cohen
  1996-04-02  0:00             ` Robert A Duff
  1 sibling, 0 replies; 80+ messages in thread
From: Norman H. Cohen @ 1996-04-02  0:00 UTC (permalink / raw)


In article <3160B2F1.260D@mcs.com>, Mike Young <mikey@mcs.com> writes: 

|> This appears somewhat frightening to me, that I might catch exceptions
|> from unknown and unspecified source at any given moment. What exactly
|> should I do with this exception aside from terminating? (How is this
|> different from SIGCHLD?) This might have the right intuitive feel if
|> this behavior applied up until some known point where parent and child
|> parts ways, but what point would that be?

You are right, this is frightening.  That is why--as you'll see if you
reread the preceding discussion carefully--this alternative (which I
called approach (b) in article <4jp17p$17vn@watnews1.watson.ibm.com>) was
rejected in the design of Ada.

--
Norman H. Cohen    ncohen@watson.ibm.com




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

* Re: some questions re. Ada/GNAT from a C++/GCC user
  1996-04-01  0:00               ` Robert A Duff
  1996-04-02  0:00                 ` Tucker Taft
@ 1996-04-02  0:00                 ` Ken Garlington
  1996-04-02  0:00                   ` Robert A Duff
  1 sibling, 1 reply; 80+ messages in thread
From: Ken Garlington @ 1996-04-02  0:00 UTC (permalink / raw)


Robert A Duff wrote:
> 
> But what about the case where I don't *want* to
> name it?  Adding lots of names doesn't make the code more readable.  You
> only want to add names when there's something conceptually name-able.

OK - I'll walk out onto the limb you've so kindly offered. I suspect that,
if you find yourself creating a new scope in "real" code that has no purpose
sufficiently cohensive as to be named, that you need to review your program
structure.

(And out come the saws... :)

> True.  But don't forget that the compiler is just checking that the
> names match up, which is useful, but it's not checking that the names
> actually tell the truth about what "a_scope" is all about.

True, just like it can't tell if any user-defined identifier has the meaning
it should. I think the consistency checking alone is very useful. Consider:

if ... then -- scope_1
   ...
   if .. then -- scope_a
      ...
   end if; -- scope_1
   ...
end if; -- scope_a

Get a page/screen break in just the wrong place, and you can easily mislead
the reader as to the structure of your program. It would be nice if Ada
allowed the naming of if/case statements, just like loops/blocks.

> Note that the block_statement *name* is option in Ada, which I think is
> good -- you can name it if there's a name the gives useful information,
> but if the name is just "The_Loop_Body" or "The_Else_Part", you can omit
> it.

The fact that local scope naming is optional isn't relevant, in my mind.
For that matter, putting a name on the end of most scopes is optional.
If I'm writing a quickie example, I might not put a name on the end of
my subprograms. But for maintainable code, I always put a name on the
end (and a comment on the begin, for that matter.) I try to name all
my scopes, in part to assure myself (and the reader) as to the legitimate
purpose of that scope, and in part to (hopefully) improve readability
of any important code. And, since I like to return strings and other
variant-sized objects from functions, I tend to have my share of block
statements.

By the way, do you have a problem with the "begin" keyword in a subprogram?
I suppose it would also be considered unnecessary, if I understand your
position correctly. What about the "exception" keyword, either in block
statements or subprograms?




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

* Re: some questions re. Ada/GNAT from a C++/GCC user
  1996-04-01  0:00     ` some questions re. Ada/GNAT from a C++/GCC user Robert A Duff
@ 1996-04-02  0:00       ` Kevin Cline
  1996-04-02  0:00         ` Robert A Duff
  0 siblings, 1 reply; 80+ messages in thread
From: Kevin Cline @ 1996-04-02  0:00 UTC (permalink / raw)


In article <Dp75DK.DMG@world.std.com>,
Robert A Duff <bobduff@world.std.com> wrote:
>Well, I'm not sure exactly what you're asking.  To print stuff out, you
>need some procedure that prints text strings, and you need some way to
>convert whatever you want to print out into a text string.  In Ada,
>'Image is used to convert simple data types like Integer and Float into
>human-readable Strings.  For complicated data types, you write your own
>Image function.  

This is a flaw in the Ada language design, IMHO.  Why should
different methods be used to externalize primitive and user-defined types?
It certainly makes program maintainence more difficult; imagine a change
from

	type sequence_number_type is 0..2**32-1

to

	type sequence_number_type is 
	record
	  time: time_type;
	  serial: 2**32-1;
	end;

If this change was not anticipated then every use of 'IMAGE to
output a sequence number must be changed to IMAGE().

>The "&" operator just concatenates Strings.  So to
>print out some stuff, you write:
>
>    Put("X = " & Integer'Image(X) & "; and List = " & Image(List));
>

Sloshing all those characters around unnecessarily gets a bit
expensive after a while.  This expression is likely to copy the list
image twice before it is actually output.
-- 
Kevin Cline




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

* Re: some questions re. Ada/GNAT from a C++/GCC user
  1996-04-02  0:00                 ` Ken Garlington
@ 1996-04-02  0:00                   ` Robert A Duff
  1996-04-02  0:00                     ` Ken Garlington
  0 siblings, 1 reply; 80+ messages in thread
From: Robert A Duff @ 1996-04-02  0:00 UTC (permalink / raw)


In article <3160E91E.1627@lfwc.lockheed.com>,
Ken Garlington  <garlingtonke@lfwc.lockheed.com> wrote:
>OK - I'll walk out onto the limb you've so kindly offered. I suspect that,
>if you find yourself creating a new scope in "real" code that has no purpose
>sufficiently cohensive as to be named, that you need to review your program
>structure.

I don't see why the desire to declare a local variable necessarily
implies that this section of code is "sufficiently cohensive as to be
named".  Here's an example:

    function Binary_Search(A: Some_Array_Type; E: Element_Type) return Boolean is
        Low: Index_Type := A'First;
        High: Index_Type := A'Last;
    begin
        while Low < High loop
            declare
                Midpoint: constant Index_Type := (Low + High) / 2;
            begin
                if A(Midpoint) = E then
                    return True;
                elsif ...
                else
                    ... -- etc.
                    -- I won't bother typing in the whole algorithm,
                    -- because then I'll be embarrassed when I get it
                    -- wrong.  ;-)
                end if;
            end;
        end loop;
        return False;
    end Binary_Search;

It doesn't seem to me that the declare block above needs a name -- it's
not that big, and its function is intimately tied to the surrounding
loop, rather than being stand-alone.

I admit that it wouldn't kill me to put Midpoint up at the top of the
procedure, but then it couldn't be a constant, which, IMHO, would damage
the readability.  No big deal, but still damage.

>(And out come the saws... :)

;-)

>True, just like it can't tell if any user-defined identifier has the meaning
>it should. I think the consistency checking alone is very useful. Consider:
>
>if ... then -- scope_1
>   ...
>   if .. then -- scope_a
>      ...
>   end if; -- scope_1
>   ...
>end if; -- scope_a
>
>Get a page/screen break in just the wrong place, and you can easily mislead
>the reader as to the structure of your program. It would be nice if Ada
>allowed the naming of if/case statements, just like loops/blocks.

Agreed.  Of course, you can always put in a block_statement if you
really want a name.  The GNAT sources often put a block_statement in
each branch of a big case_statement, purely to give a name to each
when-clause.  These block_statements often have *no* declarations and
*no* exception handlers -- they're pure syntactic sugar, which is fine.

I would like to be able to declare very-local objects without a named
scope, and without declare/begin/end.  But I agree with you -- if a name
is appropriate, it's nice to have that option as well.

>The fact that local scope naming is optional isn't relevant, in my mind.
>For that matter, putting a name on the end of most scopes is optional.
>If I'm writing a quickie example, I might not put a name on the end of
>my subprograms. But for maintainable code, I always put a name on the
>end (and a comment on the begin, for that matter.)

I actually would prefer if the name at the end of a subprogram was *not*
optional.  For quickie examples, my editor types it in automatically, so
it's not painful.  No big deal, though.

>By the way, do you have a problem with the "begin" keyword in a subprogram?
>I suppose it would also be considered unnecessary, if I understand your
>position correctly.

I'm not sure.

(Now *that's* no way to keep an argument going.  ;-) )

>... What about the "exception" keyword, either in block
>statements or subprograms?

I have no problem with the exception keyword.  To me, an exception
handler is much more of a "big deal" than a plain old variable
declaration.

However, if you look at the syntax rules for Ada 95, you'll see that the
exception handlers are *not* attached to the block,subprogram,whatever,
as they were in Ada 83.  They are attached to the statement list.  This
makes sense, since the handler applies only to exceptions raised during
the statement list.  Compare the stuff in chap 11, RM83 and RM95, and
you'll see that the description of which handler applies to what part of
the code, is somewhat simpler in RM95, because of this syntax change.
(Not really a change to the syntax, only a change to the form of the
rules.)

- Bob




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

* Re: some questions re. Ada/GNAT from a C++/GCC user
  1996-04-02  0:00       ` Kevin Cline
@ 1996-04-02  0:00         ` Robert A Duff
  0 siblings, 0 replies; 80+ messages in thread
From: Robert A Duff @ 1996-04-02  0:00 UTC (permalink / raw)


[Removed from comp.lang.c++, since I fear C++ folks are bored of this.]

In article <4jrhj0$lqd@tpd.dsccc.com>,
Kevin Cline <kcline@sun132.spd.dsccc.com> wrote:
>This is a flaw in the Ada language design, IMHO.  Why should
>different methods be used to externalize primitive and user-defined types?
>It certainly makes program maintainence more difficult; imagine a change
>from
>
>	type sequence_number_type is 0..2**32-1
>
>to
>
>	type sequence_number_type is 
>	record
>	  time: time_type;
>	  serial: 2**32-1;
>	end;
>
>If this change was not anticipated then every use of 'IMAGE to
>output a sequence number must be changed to IMAGE().

I suppose.  But you'll also have to change other stuff, like any numeric
literals, and any arrays indexed by sequence_number_type, and any
case_statements, which aren't legal when you change it to a record.
This is a much more general issue -- some prefer a language like Lisp,
which uses the same syntax for just about everything.

If Sequence_Number_Type is really an abstract data type, you'll probably
make it a private type in the first place, and then you won't have to
change anything outside the package.

Actually, I generally do *not* use Integer'Image directly -- I use a
function called Image, even for Integer types, and I use type derivation
to inherit it.  Partly for the reason you stated.  Also, Integer'Image
has an annoying habit of putting an extra unwanted blank, plus it
doesn't work for hexadecimal, and various other formatting options I
like to have.

>>The "&" operator just concatenates Strings.  So to
>>print out some stuff, you write:
>>
>>    Put("X = " & Integer'Image(X) & "; and List = " & Image(List));
>
>Sloshing all those characters around unnecessarily gets a bit
>expensive after a while.  This expression is likely to copy the list
>image twice before it is actually output.

True.  We were talking about debugging printouts, so it doesn't matter
in that context.  In a context where it *does* matter, you can call Put
once for each string.  Also, in Ada, you can define operators, so you
can play the same games as C++ does with "<<" and ">>", although you
have to use "&" or something as the operator name.  I've never done
that, because it seems ugly to me -- a matter of taste, I suppose.

- Bob




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

* Re: some questions re. Ada/GNAT from a C++/GCC user
  1996-04-02  0:00                       ` Robert A Duff
@ 1996-04-03  0:00                         ` Ken Garlington
  1996-04-09  0:00                           ` Matt Kennel
  1996-04-03  0:00                         ` David Emery
  1 sibling, 1 reply; 80+ messages in thread
From: Ken Garlington @ 1996-04-03  0:00 UTC (permalink / raw)


Robert A Duff wrote:
> 
> >What about a variable declaration that's not so plain? Would I still
> >need a block statement in that case?
> 
> I think you should be able to introduce a named block wherever you like.

OK, but what is the impact to maintenance if Ada supports both "declaration
on the fly" and "declaration within blocks"? If I introduce a "simple
declaration" (whatever that means) without the need to explicitly highlight
the declarative region, and then I start slowly increasing the complexity
of that declarative region, do I get into trouble?

My bottom line on Ada is that, for very simple programs, you type some
stuff you don't need. Never mind "declare", there's all sort of keywords
you could probably get rid off for trivial programs, and a smart compiler
could still derive the meaning (a la PL/I, which can derive meaning from
total nonsense, too!) However, those keywords do add value to the maintainer
(my opinion). With current editor technology, and the not-at-all-
uncommon phenomenon of tiny programs growing over time, I think it's a small
price to pay.

> 
> - Bob




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

* Re: some questions re. Ada/GNAT from a C++/GCC user
  1996-04-02  0:00                   ` Felaco
  1996-04-02  0:00                     ` Robert Dewar
@ 1996-04-03  0:00                     ` Mark A Biggar
  1 sibling, 0 replies; 80+ messages in thread
From: Mark A Biggar @ 1996-04-03  0:00 UTC (permalink / raw)


In article <Dp9Bxu.MBE@ssd.ray.com> bcf@ssd.ray.com (Felaco) writes:
>Anyway, it would have been *nice* if we could dispense with the 'declare' 
>and the 'end' when a new scope is introduced anyway, like in an if block 
>or a loop.  I don't see how backwards compatibility with Ada83 would be 
>compromised, or any other rules would have to change.
>ie:
>   if whatever then
>       X : Integer;
>       Y : Some_Task_Type;
>     begin
>       do_something;
>   else
>       loop
>           Y : Boolean;
>         begin
>           do_something_else;
>           exit when Y;
>       end loop;
>   end if;
>I find the above just as human-readable, and still machine-readable.  The 
>presence of the declaration signals that a new scope is introduced, the 
>begin serves as a separator and the else serves as the end.  Why not?

Hum, that may be machine readable, but its not easily machine readable.
It appears to require arbitrarily long look ahead to parse (mainly because 
of pragmas).

--
Mark Biggar
mab@wdl.loral.com







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

* Re: some questions re. Ada/GNAT from a C++/GCC user
  1996-04-01  0:00           ` Robert A Duff
@ 1996-04-03  0:00             ` Scott Leschke
  1996-04-04  0:00               ` AdaWorks
  0 siblings, 1 reply; 80+ messages in thread
From: Scott Leschke @ 1996-04-03  0:00 UTC (permalink / raw)


bobduff@world.std.com (Robert A Duff) writes:

>In article <4jmuj5$lkh@dayuc.dayton.saic.com>,
>John G. Volan  <John_Volan@ccmail.dayton.saic.com> wrote:

[bunch of stuff deleted...]

>>I assume that the only thing that would make sense would be to treat
>>every structured statement as the moral equivalent of a begin/end.

>You assume right.  That is, we're arguing about syntactic sugar; the
>semantics doesn't change if you allow declare-begin-end to be left out.

Which implies that any of these constructs could have an exception
handler region.  I've often thought that this might be nice thing
to allow in the case of loop constructs.  Syntactically I'm not sure
I'd like it for some of the other constructs like if-elsif-else or
case statements.

I find the following, er... unappealing to say the least although maybe
it's just my anal sense of style (BTW, I initially tried justifying
the exception keyword with the if-elsif-else stuff but that looked worse
IMHO :-)

if ConditionA then
      DoA;
   exception
      when ExceptionA =>

         ReportError ("A");
elsif ConditionB then
   DoB;
   exception
      when ExceptionB =>

         ReportError ("A");
else
   DoC;
   exception
      when ExceptionC =>

         ReportError ("A");
endif;


Ultimately, it would probably make little difference either way.
-- 
Scott Leschke.........................email: leschkes@cig.mot.com
Motorola, Inc............................ph: 847-632-2786
1501 W Shure Drive......................fax: 847-632-3145
Arlington Heights, IL   60004......mailstop: 1301




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

* Re: some questions re. Ada/GNAT from a C++/GCC user
  1996-04-03  0:00               ` Geert Bosch
@ 1996-04-03  0:00                 ` Robert Dewar
  0 siblings, 0 replies; 80+ messages in thread
From: Robert Dewar @ 1996-04-03  0:00 UTC (permalink / raw)


Geert said

"You still forgot to mention something:
   Also for humans (at least for this human) it is simply easier to
   parse source code that has all of the declarations in one section.
"

Absolutely, that hits it on the head! We often design in restrictions
for purely methodological reasons, even though they do not help the
compiler, and may actually complicate the compiler.

For example, in Ada, we do not permit a goto to leave a procedure body.
Allowing this is perfectly easy, but it creates a program structure
that we prefer to to permit.





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

* Re: some questions re. Ada/GNAT from a C++/GCC user
  1996-04-02  0:00                       ` Robert A Duff
  1996-04-03  0:00                         ` Ken Garlington
@ 1996-04-03  0:00                         ` David Emery
  1 sibling, 0 replies; 80+ messages in thread
From: David Emery @ 1996-04-03  0:00 UTC (permalink / raw)


On the topic of 'named things', one area where Ada could have improved,
in my opinion, is to allow an optional name at the end of a record
declaration. I've seen some code with a lot of relatively complex
record declarations, where this would have helped readability.  As a
coding convention, I usually provide the comment after "end record" when
the record declaration is "long" (greater than 10 lines, depending on 
context).  

Here's what I would have liked:

   type Very_Complex_record (Discr1, Discr2 : integer; Has_Blotz : boolean)
      is record
         f1 : t1;
         f2 : t2;
         case has_blotz is
            when true =>   
               case Discr1 is
                  when 3 | 6 | 9  =>
                     Mungification : Whatever;
                  when 42 =>
                     Life_The_Universe_and_Everything : Boolean := False;
                  when others =>
                     case Disrc2 is
                        when 69 =>    
                           null;    -- what did you expect??
                        when 100 => 
                           George_Burnes_Cigars : Stogies;
                        when others =>
                           Shoe_Size : natural;
                     end case;
               end case;
            when false =>
               Other_Stuff : Some_Other_Record (Sex => true);
         end case;
      end record Very_Complex_Record;  -- not legal Ada, at not least in
               -- this millennium...

               dave




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

* Re: some questions re. Ada/GNAT from a C++/GCC user
  1996-04-02  0:00             ` Glenn H. Porter
  1996-04-02  0:00               ` Jonas Nygren
  1996-04-02  0:00               ` Robert Dewar
@ 1996-04-03  0:00               ` Geert Bosch
  1996-04-03  0:00                 ` Robert Dewar
  2 siblings, 1 reply; 80+ messages in thread
From: Geert Bosch @ 1996-04-03  0:00 UTC (permalink / raw)


Glenn H. Porter (ghporter@NetXpress.com) wrote:
`` Most folks that responded to this missed something.  That it is
   simply easier to parse source code that has all of the declarations
   in one section while building a symbol table. ''

You still forgot to mention something: 
   Also for humans (at least for this human) it is simply easier to
   parse source code that has all of the declarations in one section.

Regards,
   Geert

PS. I'm afraid I also build symbol tables while parsing the source ;-)




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

* Re: some questions re. Ada/GNAT from a C++/GCC user
  1996-03-29  0:00 ` Robert A Duff
                     ` (3 preceding siblings ...)
  1996-04-01  0:00   ` Iterators (was Re: some questions re. Ada/GNAT from a C++/GCC user) Robert I. Eachus
@ 1996-04-04  0:00   ` Jon S Anthony
  4 siblings, 0 replies; 80+ messages in thread
From: Jon S Anthony @ 1996-04-04  0:00 UTC (permalink / raw)


In article <ROGOFF.96Mar29163036@sccm.Stanford.EDU> rogoff@sccm.Stanford.EDU (Brian Rogoff) writes:

>    > Is there any way in Ada to iterate abstractly over the contents of a 
>    > container,...
> 
>    Several ways:
> [typical stuff for doing this...]
>
> Jon Anthony, where are you? :-)
> (FYI, Jon has a very nice approach to simulating Sather iters in Ada
> 95. I'll leave it for him to post.)

Thanks Brian!

OK, this has come up before and I've sent it out to various folk who
were interested, but I will just append it to this post.  It was done
around a year or so ago.  Note that the approach is reasonably neat
(IMO, of course!), but suffers some drawbacks vis-a-vis the real thing
in Sather.  In particular, an exception is involved (though it looks
like 3.04 Gnat is an example going in the right direction for making
simple cases very efficient) and you have to build the implementations
at a lower level (you joe programmer build the state machines for the
iterators...) - the Sather compiler does this dross work for you.
 
>    None of these is as pretty as the Sather solution, but they achieve the
>    main goal, which is to have the module that defines the data structure
>    define how to loop, and the clients define what they do at each
>    iteration.
> 
> The Sather iteration abstraction is great, and should be considered for 
> inclusion in future variants of Ada. Maybe now that GNAT exists, someone 
> will make an iter enhanced Ada like language (Sada? Sadie?).

Yes, the Sather stuff is really quite excellent.  In fact, in general
Sather is quite excellent.  Re new lang: Why not Sade? ;-)


>    - Bob
> 
>    P.S. I hope you enjoy Ada.  Why not get a copy of GNAT, and try it out?
> 
> Seconded!
> 
> -- Brian

Three-peted!

--------------Sather iterators in Ada95-------------------

Hi,

I've been looking into Sather quite a lot recently (it is a very good
language design) and in particular Sather iterators.  These have been
discussed a bit in comp.object and comp.lang.ada (as well as
comp.lang.sather!), so you may be a bit familiar with them.  Anyway, I
also retrieved and read over the technical report TR-93-045 (very good
paper from the Sather home page) and while reading this it occured to
me that Sather iterators were very close to a kind of special case use
of Ada (all references are to Ada95) protected types. Conceptually,
Sather iterators work as follows:

Sather has a single looping construct: loop stmt_seq end.  It also
allows for "classes" to have special operations called iterators which
are pretty much like any other Sather routine except

a) They are indicated by a "!" in their declaration: my_iter! (...) is ...end

b) They may only be called from within a loop statement

c) Any iterator in a loop is implicitly declared and initialized at
   the beginning of the loop and only exists for the life of the loop.

d) They may "yield" as well as "quit" (return...)

e) Between calls they *maintain state* (when yielding)

f) when any one quits, the loop is exited.


The canonical implementation would be coroutines (where a loop is also
the "main" coroutine which controls the order of "calls").  At this
point it occured to me that you could sort of do this with tasks but
they are much too expensive for a simple efficient iteration scheme
and entries don't allow for composing - they can't return values.
Then it occured to me that Sather iterators are really simple state
machines and that this would be an efficient implementation.  Of
course, at the end of the paper the authors also point this out and my
guess is that the Sather compiler implements them as such.

The paper also points out many of the problems of the cursors/riders,
streams, and blocks/lambda-expressions style of iterators.  Some of
these problems do not exist with these constructs in Ada.  For
example, in Ada (thanks to separation of class and module!), cursors
do not "require maintaining a parallel cursor object hierarchy
alongside each container class hierarchy".  However, many of the
problems mentioned are equally true of such iteration techniques in
Ada as well.  In general, Sather iterators do not have any of the
problems mentioned and hence are indeed a very clever and well thought
out scheme for general iteration.

What I am proposing here is a canonical idiom to code Sather iterators
in Ada using protected types and access discriminants.  The protected
types encapsulate iter operations and the access discriminants allow
them to

a) have and maintain protected state (between calls),
b) be implicitly initialized when declared,
c) force them to operate on a particular instance of a "class" and
d) "disappear" at the end of a loop decl block.

One other nice aspect is that since protected types are used, your
iterators are tasking safe for "free".  At present this does not
appear to be true of Sather iterators (but that is OK since Sather is
not multithreaded).

I will also show a few examples, which are direct cribs of the Sather
iterators presented in the TR paper.  All of this compiles and
executes just fine with Gnat.


Let's start with an encapsulated resource which can benefit from
iteration:


package Obj_Class is
    type element_type is ...;  -- The elements of Obj_Type...
    type Obj_Type is ...;      -- Irrelevant what it is...
    ...                        -- Primitive operations for Obj_Type
end Obj_Class;


We could place all the iterator stuff in Obj_Class, but maybe it makes
more sense to put them in a child package (then again, maybe not...),
but you are free to do whatever makes the most sense for a given
situation.  Also, these packages could be generic for added
flexibility.


package Obj_Class.Iterators is

    Quit : exception;

    type State_Type is (Yield, Done);

    type Iter_State_Type ( Obj : access Obj_Type ) is tagged limited record
	State   : State_Type := Yield;
	Loc     : Location_type := first_loc_function_for_obj_class;
    end record;


    type Iterator;

    protected type Iter_Op_Type ( Iter : access Iterator ) is
	-- Various iteration operations...
    end Iter_Update_Type;

    type Iterator is new Iter_State_Type with record
	Res : Element_Type := null_element;
	Op  : Iter_Op_Type(Iterator'Access);
    end record;

end Obj_Class.Iterators;


A few notes on this.  First, we could have Iter_State_Type be
parameterized by Obj_Type'Class for added flexibility - any
descendents of Obj_Type could use these iterators.

Second, we can have different iterators for each sort of iteration
operation: reading, updating - whatever.  You would then have, say,
Read_Iterator/Iter_Read_Type and Update_Iterator/Iter_Update_Type (in
place of Iterator/Iter_Op_type).  This seems like a better idea than
just dumping all possible iteration operations in one iterator type,
but it probably depends on the situation.

Third, the reason why we have an extra separate enclosing record,
Iter_State_Type, instead of using the data region of a protected type,
is to allow for the iteration operations to be functions.  Function
operations of protected types (for very good real time reasons!)
cannot update any internal state of the type, but iterators will
typically need to do this.

Fourth, the initial values for State, Loc, and Res, guarantee proper
implicit initialization at the point an instance of Iterator is
declared.

The body for the above will look something like:


package body Obj_Class.Iterators is

    procedure Forward ( Iter : access Iter_State_Type'Class ) is
    begin
	if not At_End(Iter.Loc) then
	    Iter.Loc := Next_Iter_Loc(Iter.Loc);
	else
	    Iter.State := Done;
	end if;
    end Forward;
    pragma Inline(Forward);


    protected body Iter_Op_Type is

	procedure Op_1 (...) is
	Begin
	    case Iter.State is
		when Yield =>
		    -- action on/with Iter.Obj at location Iter.Loc
		    Forward(Iter);
		when Done =>
		    raise Quit;
	    end case;
	end Op_1;

        function Op_2 ( e : Element_Type ) return Element_Type is
        Begin
            Case Iter.State Is
                when Yield =>
                    Iter.Res := do_something(Iter.Res, e);
                    return Iter.Res;
                when Done =>
                    raise Quit;
            end case;
        end Op_2;
 
        ... -- Other ops for this iteration type

    END iter_update_type;

END Obj_Class.Iterators;


Note that the particular Obj (of Iter.Obj) is given when an instance
of the Iterator is declared.  Also note that all the Iter.* entities
maintain their state _between calls_ to Op_1, Op_2, etc. for _each_
particular instance of the iterator.


With these resources a user can then use them to do general iteration
like that available in Sather.  For example:


with Obj_Class.Iterators;  use Obj_Class.Iterators;
with Whack_It;
procedure test is

    Obj_1 : Obj_Type := new Obj_Type'(...);

begin

    declare
	Obj_2  : aliased Obj_Type := (others => ...);

	-- Declare an iterator for Obj_1 and Obj_2 and initialize them
	--
	Iter_1 : Iterator(Obj_1'Access);
	Iter_2 : Iterator(Obj_2'Access);
    begin
	loop
	    -- Each time through the loop we are able to twiddle
	    -- Obj_1 via iterator Op_1 with the results of Obj_2
	    -- via iterator Op_2.
	    --
	    Iter_1.Op.Op_1(Whack_It(Iter_2.Op.Op_2));
	end loop;
    exception
	when Quit => null;
    end;

    -- Iterators are deallocated and gone, thus preserving
    -- various semantic integrity aspects per Sather TR 3.2
    -- and 3.3

end;


Notice that like Sather iterators (and unlike cursors) these protected
type iterators (PT iters) are part of the container class itself; PT
iters mangage their own storage and in the example they use the stack
and not the heap; the state of PT iters is confined to a single loop
(though this _can_ be violated); and PT iters may be "arbitrarily"
nested and support recursion (an example of which follows).

As noted, there is a hole in this in that unlike Sather iters, PT
iters can be "passed around in a half-consumed state".  This can
happen if the iters are declared more global than the loop that will
use them.  If the style of the above example is used this can never
happen (of course, this is not as good as the Sather iter
_guarantee_...)

The other obvious question mark is that Quit exception.  While this
could (reasonably easily) be optimized away into a simple exit for the
loop, I doubt that all compilers would support this level of "quality
of service" in this respect.  Unless, of course, this becomes a
popular idiom! :-) But, in general, I think for most cases it will be
quite efficient enough (exceptions can be handled _very_ quickly if
caught by the inner most enclosing block as they do not need to unwind
and do all sorts of other nasty things...)  Beyond any efficiency
question there is the hole that the user must remember to catch the
thing in loop block!  Again, in Sather this is just part of the
semantics of iterators and is a non-issue.


OK, here are some real live working examples.  Both crib examples from
the TR paper and are pretty self explanatory (note that the so called
"same fringe problem" is also easily handled with PT iters).

The Sieve of Eratosthenes example requires a little discussion,
especially if you are familiar with the Sather version.  In Sather,
recursive calls of an iterator create new _invocation_ instances
(conceptually they create a new instance of the coroutine for the
iterator) all of which are able to maintain their own private state.
There is no simple way to do this using only PT iters (you would need
to invoke the god of tasking).  Hence, for the Ada PT version, the
state for the divisor "gauntlet" is made an explicit part of the
declaration of the iter instance (the d field in prime_generator)
while in the Sather version it is implicit in all the created
invocation instances (which is definitely a nifty trick).

------- Start Examples: all work with Gnat 2.03 on Sparc.SunOS ----------

package P is
--
-- Check out protected types as Sather Iterators.
    
    subtype Element_Type is Integer;


    subtype Range_Type is Integer range 1..10;
    type Obj_Type is array (Range_Type) of Element_Type;



    Quit : exception;

    type State_Type is (Yield, Done);

    type Iter_State_Type ( Obj : access Obj_Type ) is tagged limited record
	State   : State_Type := Yield;
	Loc     : Range_Type := Range_Type'First;
    end record;


    type Update_Iterator;

    protected type Iter_Update_Type ( Iter : access Update_Iterator ) is
	procedure Set_Elts ( E: Element_Type );
        function  Sum ( Summand : Element_Type ) return Element_Type;
    end Iter_Update_Type;

    type Update_Iterator is new Iter_State_Type with record
	Res : Element_Type := 0;
	Op  : Iter_Update_Type(Update_Iterator'Access);
    end record;


    protected type Iter_Read_Type ( Iter : access Iter_State_Type'Class ) is
	function  Elts return Element_Type;
    end Iter_Read_Type;

    type Read_Iterator is new Iter_State_Type with record
	Op  : Iter_Read_Type(Read_Iterator'Access);
    end record;


end P;


package body P is
--
-- Check out protected types as Sather Iterators.


    procedure Forward ( Iter : access Iter_State_Type'Class ) is
    begin
	if Iter.Loc < Range_Type'Last then
	    Iter.Loc := Iter.Loc + 1;
	else
	    Iter.State := Done;
	end if;
    end Forward;
    pragma Inline(Forward);



    protected body Iter_Update_Type is

	procedure Set_Elts ( E: Element_Type ) is 
	begin
	    case Iter.State is
		when Yield =>
		    Iter.Obj(Iter.Loc) := E;
		    Forward(Iter);
		when Done =>
		    raise Quit;
	    end case;
	end Set_Elts;


	function  Sum ( Summand : Element_Type ) return Element_Type is 
	begin
	    case Iter.State is
		when Yield =>
		    Iter.Res := Iter.Res + Summand;
		    return Iter.Res;
		when Done =>
		    raise Quit;
	    end case;
	end Sum;

    end Iter_Update_Type;



    protected body Iter_Read_Type is

	function Elts return Element_Type is
	    E : Element_Type;
	begin
	    case Iter.State is
		when Yield =>
		    E := Iter.Obj(Iter.Loc);
		    Forward(Iter);
		    return E;
		when Done =>
		    raise Quit;
	    end case;
	end Elts;

    end Iter_Read_Type;


end P;


with Text_Io;
with P;  use P;

procedure Test_New_Iters is
--
-- Test Sather style iterators in Ada95

    
    A     : aliased Obj_Type := (others => 1);
    B     : aliased Obj_Type := (1, 2, 3, 4, 5, 6, 7, 8, others => 9);
    Ten_A : Obj_Type := (10, 20, 30, 40, 50, 60, 70, 80, others => 90);

    Overall_Passed : Boolean := True;

    procedure Failed ( Code : Integer ) is
    begin
	Text_Io.Put_Line("***FAILED test " & Integer'Image(Code));
	Overall_Passed := False;
    end Failed;


begin

    declare
	Au_Iter : Update_Iterator(A'Access);
	Ar_Iter : Read_Iterator(A'Access);
	Br_Iter : Read_Iterator(B'Access);
    begin
	loop
	    -- Standard matrix multiplication by scalar: A := B*i
	    --
	    Au_Iter.Op.Set_Elts(Br_Iter.Op.Elts * 10);
 	    Text_Io.Put_Line(Integer'Image(Ar_Iter.Op.Elts));

	end loop;
    exception
	when Quit =>
	    if A /= Ten_A then
		Failed(1);
	    end if;
    end;


    declare
	Au_Iter : Update_Iterator(A'Access);
	Ar_Iter : Read_Iterator(A'Access);
	Br_Iter : Read_Iterator(B'Access);
	X       : Element_Type := 0;
    begin
	loop
	    -- Compute the sum of the products of the elements of A & B
	    --
	    X := Au_Iter.Op.Sum(Ar_Iter.Op.Elts * Br_Iter.Op.Elts);

	end loop;
    exception
	when Quit =>
 	    Text_Io.Put_Line("sum A(i)*B(i) =" & Integer'Image(X));
	    if X /= 3660 then
		Failed(2);
	    end if;
    end;

    if Overall_Passed then
        Text_Io.Put_Line("PASSED");
    end if;

end Test_New_Iters;


-----------------Start Sieve Example-------------------

with Text_Io;

procedure Primes is
--
-- Sieve of Eratosthenes using Sather like iterators...


    type Prime_Generator;

    protected type Siever ( Iter : access Prime_Generator ) is
	function Sieve ( Aprime : Positive; I : Positive ) return Boolean;
	function Gen return Positive;
    end Siever;

    type Divisors is array (Positive range <>) of Natural;
    type Prime_Generator (Count : Positive) is limited record
	D   : Divisors(1..Count) := (others => 0);
	Res : Positive := 1;
	Op  : Siever(Prime_Generator'Access);
    end record;


    Quit : exception;

    protected body Siever is

	function Sieve ( Aprime : Positive; I : Positive ) return Boolean is
	begin
	    if Iter.D(I) = 0 then
		Iter.D(I) := Aprime;
		return True;
	    elsif Aprime mod Iter.D(I) = 0 then
		return False;
	    else
		return Sieve(Aprime, I+1);
	    end if;
	end Sieve;

	function Gen return Positive is
	begin
	    Iter.Res := Iter.Res + 1;
	    if Iter.D(Iter.Count) /= 0 then
		raise Quit;
	    elsif Sieve(Iter.Res, 1) then
		return Iter.Res;
	    else
		return Gen;
	    end if;
	end Gen;
		
    end Siever;


begin

    declare
	Primes1 : Prime_Generator(10);
    begin
	loop
	    Text_Io.Put_Line(Integer'Image(Primes1.Op.Gen));
	end loop;
    exception
	when Quit => null;
    end;


    declare
	Primes1 : Prime_Generator(50);
    begin
	loop
	    Text_Io.Put_Line(Integer'Image(Primes1.Op.Gen));
	end loop;
    exception
	when Quit => null;
    end;

end Primes;
-- 
Jon Anthony
Organon Motives, Inc.
1 Williston Road, Suite 4
Belmont, MA 02178

617.484.3383
jsa@organon.com





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

* Re: some questions re. Ada/GNAT from a C++/GCC user
  1996-04-03  0:00             ` Scott Leschke
@ 1996-04-04  0:00               ` AdaWorks
  0 siblings, 0 replies; 80+ messages in thread
From: AdaWorks @ 1996-04-04  0:00 UTC (permalink / raw)


Scott Leschke (leschkes@ferret.cig.mot.com) wrote:

: if ConditionA then
:       DoA;
:    exception
:       when ExceptionA =>

  Actually, I can think of examples from programming languages 
  which do permits just this sort of thing.  The one which comes
  most quickly to mind is from COBOL,

        Read ... On Size-Error ...

      or

        Search ... At End ...

      among others.

  Even some variants of BASIC have an ON ERROR ... statement.

  The virtues, if any, of the declare block are not limited to
  exception handling.  A simple,

              begin
                 ...
              exception
                 ...
              end;

  will accomplish that purpose.  Though I prefer infrequent usage of
  ad hoc constructs whether explict as "declare blocks" or implicit via
  in-line declarations, I tend to believe that when such constructs are
  appropriate, the declare block will provide greater service over the full 
  life-cycle of a deployed software product.  While brevity may be the
  soul of wit, clarity would seem to be the soul of understanding.
  
  In the end, however, we each gravitate toward the idiom that feels
  most comfortable. A great Rabbi is reported to have said 
  (paraphrasing), "For those who believe an idea to be true,
  no proof is necessary.  For those who do not, no proof is possible."

  Richard
  
-- 

richard@adaworks.com
AdaWorks Software Engineering
Suite 27
2555 Park Boulevard
Palo Alto, CA 94306
(415) 328-1815
FAX  328-1112




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

* Re: some questions re. Ada/GNAT from a C++/GCC user
  1996-04-03  0:00                         ` Ken Garlington
@ 1996-04-09  0:00                           ` Matt Kennel
  0 siblings, 0 replies; 80+ messages in thread
From: Matt Kennel @ 1996-04-09  0:00 UTC (permalink / raw)


Ken Garlington (garlingtonke@lfwc.lockheed.com) wrote:
: Robert A Duff wrote:
: > 
: > >What about a variable declaration that's not so plain? Would I still
: > >need a block statement in that case?
: > 
: > I think you should be able to introduce a named block wherever you like.

: OK, but what is the impact to maintenance if Ada supports both "declaration
: on the fly" and "declaration within blocks"? If I introduce a "simple
: declaration" (whatever that means) without the need to explicitly highlight
: the declarative region, and then I start slowly increasing the complexity
: of that declarative region, do I get into trouble?

My personal experience with a modern OO language which has only "declare
on the fly" and even static type inference ("don't declare manually at
all") is that these issues matter very little, as they should all be
localized inside a single implementation routine. 

What matters *much* more is to keep track of the declaration of routines
and the combination of routines which make up a class---in Ada, 
a package. 


Bottom line: 

	I think it advisable to be "liberal" inside the implementation of 
        subroutines for programmer convenience, but to be "anal" and "strict"
        in the package and routine interfaces.

: > 
: > - Bob




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

end of thread, other threads:[~1996-04-09  0:00 UTC | newest]

Thread overview: 80+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
1996-03-27  0:00 some questions re. Ada/GNAT from a C++/GCC user Bill Newman
1996-03-27  0:00 ` Robert Dewar
1996-03-28  0:00   ` Brian Rogoff
1996-03-29  0:00     ` John G. Volan
1996-03-30  0:00       ` Robert A Duff
1996-03-31  0:00         ` John G. Volan
1996-03-31  0:00           ` Mike Young
1996-04-02  0:00             ` Glenn H. Porter
1996-04-02  0:00               ` Jonas Nygren
1996-04-02  0:00               ` Robert Dewar
1996-04-03  0:00               ` Geert Bosch
1996-04-03  0:00                 ` Robert Dewar
1996-04-01  0:00           ` Robert A Duff
1996-04-03  0:00             ` Scott Leschke
1996-04-04  0:00               ` AdaWorks
1996-04-01  0:00           ` Bruce.Conroy
1996-03-31  0:00         ` Robert Dewar
1996-04-01  0:00           ` Norman H. Cohen
1996-03-30  0:00       ` Mike Young
1996-03-30  0:00         ` Ted Dennison
1996-03-31  0:00           ` Mike Young
1996-04-01  0:00       ` Norman H. Cohen
1996-04-01  0:00         ` Robert A Duff
1996-04-01  0:00           ` Mike Young
1996-04-02  0:00             ` Norman H. Cohen
1996-04-02  0:00             ` Robert A Duff
1996-04-01  0:00         ` Mike Young
1996-04-02  0:00           ` David Shochat
1996-04-02  0:00             ` Mike Young
1996-04-02  0:00           ` Norman H. Cohen
1996-04-02  0:00           ` Robert Dewar
1996-03-28  0:00   ` Norman H. Cohen
1996-03-28  0:00 ` Scott Leschke
1996-03-29  0:00   ` Robert A Duff
1996-03-30  0:00     ` Richard Pitre
1996-03-30  0:00       ` Robert A Duff
1996-03-31  0:00         ` AdaWorks
1996-04-01  0:00           ` Robert A Duff
1996-04-01  0:00             ` Ken Garlington
1996-04-01  0:00               ` Robert A Duff
1996-04-02  0:00                 ` Tucker Taft
1996-04-02  0:00                   ` Felaco
1996-04-02  0:00                     ` Robert Dewar
1996-04-03  0:00                     ` Mark A Biggar
1996-04-02  0:00                 ` Ken Garlington
1996-04-02  0:00                   ` Robert A Duff
1996-04-02  0:00                     ` Ken Garlington
1996-04-02  0:00                       ` Robert A Duff
1996-04-03  0:00                         ` Ken Garlington
1996-04-09  0:00                           ` Matt Kennel
1996-04-03  0:00                         ` David Emery
1996-04-01  0:00             ` Norman H. Cohen
1996-04-01  0:00             ` AdaWorks
1996-04-01  0:00               ` Mike Young
1996-04-02  0:00                 ` AdaWorks
1996-04-02  0:00                 ` Robert Dewar
1996-04-01  0:00         ` Richard A. O'Keefe
1996-04-01  0:00           ` Robert A Duff
1996-04-01  0:00         ` Robert Dewar
1996-04-02  0:00       ` Robert I. Eachus
1996-03-29  0:00   ` Robert I. Eachus
1996-03-29  0:00   ` Bill Newman
1996-03-28  0:00 ` Ted Dennison
1996-03-29  0:00   ` Adam Beneschan
1996-03-29  0:00 ` Robert A Duff
1996-03-29  0:00   ` Brian Rogoff
1996-04-01  0:00     ` Mark A Biggar
1996-04-01  0:00       ` Robert A Duff
1996-03-30  0:00   ` Iterators (was Re: some questions re. Ada/GNAT from a C++/GCC user) Robert I. Eachus
1996-03-31  0:00     ` Mike Young
1996-03-31  0:00       ` Fergus Henderson
     [not found]   ` <4jlj79$h1k@Nntp1.mcs.net>
1996-04-01  0:00     ` some questions re. Ada/GNAT from a C++/GCC user Robert A Duff
1996-04-02  0:00       ` Kevin Cline
1996-04-02  0:00         ` Robert A Duff
1996-04-01  0:00   ` Iterators (was Re: some questions re. Ada/GNAT from a C++/GCC user) Robert I. Eachus
1996-04-04  0:00   ` some questions re. Ada/GNAT from a C++/GCC user Jon S Anthony
1996-03-30  0:00 ` Simon Wright
1996-04-01  0:00 ` Laurent Guerby
1996-04-01  0:00   ` Robert A Duff
  -- strict thread matches above, loose matches on Subject: below --
1996-03-28  0:00 Simon Johnston

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