comp.lang.ada
 help / color / mirror / Atom feed
From: bobduff@world.std.com (Robert A Duff)
Subject: Re: some questions re. Ada/GNAT from a C++/GCC user
Date: 1996/03/29
Date: 1996-03-29T00:00:00+00:00	[thread overview]
Message-ID: <Dp1oAw.7Cz@world.std.com> (raw)
In-Reply-To: wnewmanDoxrCp.DKv@netcom.com

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?




  parent reply	other threads:[~1996-03-29  0:00 UTC|newest]

Thread overview: 80+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
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   ` Norman H. Cohen
1996-03-28  0:00   ` Brian Rogoff
1996-03-29  0:00     ` John G. Volan
1996-03-30  0:00       ` Mike Young
1996-03-30  0:00         ` Ted Dennison
1996-03-31  0:00           ` Mike Young
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               ` Robert Dewar
1996-04-02  0:00               ` Jonas Nygren
1996-04-03  0:00               ` Geert Bosch
1996-04-03  0:00                 ` Robert Dewar
1996-04-01  0:00           ` Bruce.Conroy
1996-04-01  0:00           ` Robert A Duff
1996-04-03  0:00             ` Scott Leschke
1996-04-04  0:00               ` AdaWorks
1996-03-31  0:00         ` Robert Dewar
1996-04-01  0:00           ` Norman H. Cohen
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           ` Norman H. Cohen
1996-04-02  0:00           ` David Shochat
1996-04-02  0:00             ` Mike Young
1996-04-02  0:00           ` Robert Dewar
1996-03-28  0:00 ` Ted Dennison
1996-03-29  0:00   ` Adam Beneschan
1996-03-28  0:00 ` Scott Leschke
1996-03-29  0:00   ` Robert I. Eachus
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             ` AdaWorks
1996-04-01  0:00               ` Mike Young
1996-04-02  0:00                 ` Robert Dewar
1996-04-02  0:00                 ` AdaWorks
1996-04-01  0:00             ` Norman H. Cohen
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         ` 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   ` Bill Newman
1996-03-29  0:00 ` Robert A Duff [this message]
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
1996-04-01  0:00   ` Robert I. Eachus
     [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-04  0:00   ` 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
replies disabled

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