comp.lang.ada
 help / color / mirror / Atom feed
* Ada FAQ: Programming with Ada (part 1 of 3)
@ 1995-01-19 18:04 Magnus Kempe
  0 siblings, 0 replies; 3+ messages in thread
From: Magnus Kempe @ 1995-01-19 18:04 UTC (permalink / raw)


Archive-name: computer-lang/Ada/programming/part1
Comp-lang-ada-archive-name: programming/part1
Posting-Frequency: monthly
Last-modified: 19 January 1995
Last-posted: the epoch

                               Ada Programmer'S
                       Frequently Asked Questions (FAQ)

This is part 1 of a 3-part posting.
Part 2 begins with question 5.7.
Part 3 begins with question 9.6.
They should be the next postings in this thread.

   IMPORTANT NOTE: No FAQ can substitute for real teaching and
   documentation. There is an annotated list of Ada books in the
   companion comp.lang.ada FAQ.

Introduction

   Ada is an advanced, modern programming language, designed and
   standardized to support widely recognized software engineering
   principles: reliability, portability, modularity, reusability,
   programming as a human activity, efficiency, maintainability,
   information hiding, abstract data types, concurrent programming,
   object-oriented programming, etc. All Ada compilers must pass a
   validation test.

   Ada is defined by an international standard (the language reference
   manual, or LRM). Ada is taught and used all around the world (not just
   in the USA).

   The latest version of this FAQ is always accessible through WWW as
   http://lglwww.epfl.ch/Ada/FAQ/programming.html

Maintenance

   This FAQ is maintained on an individual volunteer basis, by Magnus
   Kempe (Magnus.Kempe@di.epfl.ch). [Note: This is done as a hobby, not
   in my capacity as an employee at the Swiss Federal Institute of
   Technology. --MK]

   The coding style used in most of the example Ada code is my own, and
   you'll have to live with it (you may want to adopt it :-).


     _________________________________________________________________

   Opinions (if any) expressed are those of the submitters and/or
   maintainer.
     _________________________________________________________________


Table of Contents:


     * 1: Recent changes to the FAQ

     * 2: Information about this document

     * 3: Elementary Questions
          + 3.1: How do I make operations directly visible without
            "use"ing a package?
          + 3.2: How do I assign to an array of length 1?
          + 3.3: How do I create a C-style nul-terminated string?
          + 3.4: How can I create an array of strings of various length?
          + 3.5: I know an exception is raised, but my program quits with
            no warning. Why?
          + 3.6: I have only one task in my program, but it doesn't seem
            to run. Why?
          + 3.7: How do I increase the stack size for a task?
          + 3.8: What's the difference between a type conversion and a
            qualifier?
          + 3.9: How do I avoid the potential space in front of
            Integer'Image?
          + 3.10: Why is an exception raised when giving a default
            discriminant?
          + 3.11: I am learning Ada. Can I experiment with a game
            program?


     * 4: Advantages of Ada
          + 4.1: Why use Ada?
          + 4.2: Ada seems large and complex, why is it this way?


     * 5: Object-Oriented Programming with Ada
          + 5.1: Why does Ada have "tagged types" instead of classes?
          + 5.2: Variant records seem like a dead feature now? When
            should I use them instead of tagged types?
          + 5.3: What is meant by "interface inheritance" and how does
            Ada support it?
          + 5.4: How do you do multiple inheritance in Ada 9X?
          + 5.5: Why are Controlled types so, well, strange?
          + 5.6: What do "covariance" and "contravariance" mean, and does
            Ada support either or both?
          + 5.7: What is meant by upcasting/expanding and
            downcasting/narrowing?
          + 5.8: How does Ada do "narrowing"?


     * 6: Ada Numerics
          + 6.1: Where can I find anonymous ftp sites for Ada math
            packages? In particular where are the random number
            generators?
          + 6.2: How can I write portable code in Ada 83 using predefined
            types like Float and Long_Float? Likewise, how can I write
            portable code that uses Math functions like Sin and Log that
            are defined for Float and Long_Float?
          + 6.3: Where's a good place to start learning the Ada 95
            numerics model?
          + 6.4: How do I get Real valued and Complex valued math
            functions in Ada 95?
          + 6.5: What libraries or public algorithms exist for Ada?


     * 7: Efficiency of Ada Constructs
          + 7.1: How much extra overhead do generics have?


     * 8: Advanced Programming Techniques with Ada
          + 8.1: Does Ada have automatic constructors and destructors?
          + 8.2: How can I redefine assignment operations?
          + 8.3: Should I stick to a one package, one type approach while
            writing Ada software?
          + 8.4: What is the "Beaujolais Effect"?
          + 8.5: What about the "Ripple Effect"?


     * 9: Ada and Other Programming Languages
          + 9.1: Where can I find programs that will translate from [some
            language] to Ada?
          + 9.2: How can I convert Ada 83 sources to Ada 9X?
          + 9.3: I hear that Ada is slower than Fortran or C, is that
            true?
          + 9.4: Isn't Ada less "elegant" than Eiffel?
          + 9.5: Are there any papers detailing the differences between
            Ada and C++?
          + 9.6: I keep hearing that Ada is a "strongly typed language",
            but it seems different from what's meant in C++. Are they
            different?
          + 9.7: I'm told Ada does all sorts of static type checking, but
            can't you get the same effect using a tool like "lint" with
            C?
          + 9.8: Does Ada have something like the Standard Template
            Library (STL) in C++, or like the components one finds in
            Smalltalk environments?
          + 9.9: Where can I find the equivalent of "printf" in Ada?


     * 10: Interfacing with Ada
          + 10.1: I am writing software that used the Distributed
            Interactive Simulation (DIS) interface, does an interface
            exist in Ada?
          + 10.2: Is there any support for Common Object Request Broker
            Architecture (CORBA) for Ada 9X?


     * 11: Finding Additional Information
          + 11.1: Where can I find Ada books?
          + 11.2: Are there other Ada-related FAQs?
          + 11.3: What is the "Ada WWW Server"?


     * 12: Pretty-printing Ada Source Code
          + 12.1: Is there software that generates a pretty PostScript
            file from Ada source code?
          + 12.2: I use vgrind to do "pretty printing" of my source. Is
            there a vgrind definition for Ada?
          + 12.3: How about a source code reformatter?


     * 13: Common Confusions 
          + 13.1: Wasn't Ada designed by some committee? What kind of a
            language could you possibly get from a committee?
          + 13.2: I've heard the DoD is dropping all Military standards
            to reduce costs, doesn't that mean the mandate to use Ada
            goes away too?


     * 14: Credits

     * 15: Copying this FAQ


     _________________________________________________________________


1: Recent changes to the FAQ


     * 950116: converting Ada 83 code to Ada 9X.
     * 950109: more on DIS.
     * 950106: lengthy code sections extracted and put on FTP server.
     * 950105: printf solution; update on exception traces and vgrind.
     * 950104: links from TOC to all questions.
     * 941222: pretty-printing solutions.
     * 941220: numbered all questions; various updates.
     * 941215: one-element aggregates.
     * 941212: additional elementary questions, submitted by Paul Pukite.
     * 941212: examining the so-called "elegance" of Eiffel (vs. Ada).
     * 941212: integrated "Ada Numerics" section contributed by Jonathan
       Parker.
     * 941208: added "default discriminant" and "Beaujolais effect."
     * December 1994: more re-organization, HTML'ized.
     * October 1994: FAQ format by Magnus Kempe.
     * September 1994: first draft by Dave Weller.


   What's important and missing:
     * everything, life, and 42


     _________________________________________________________________


2: Information about this document

   This file is posted monthly to comp.lang.ada (and will ALSO be posted
   to news.answers, and comp.answers, as soon as it is approved in its
   format).

   This document has a home on the Ada WWW Server, in hypertext format,
   URL http://lglwww.epfl.ch/Ada/FAQ/programming.html
   The text-only version is also available in directory
   ftp://lglftp.epfl.ch/pub/Ada/FAQ

   When posted in *.answers, it will be available on rtfm.mit.edu, which
   archives FAQ files posted to news.answers; see
   ftp://rtfm.mit.edu/pub/usenet-by-group/news.answers/comp-lang-ada

   Magnus Kempe maintains this document; it's not a job, it's a hobby.
   Feedback (corrections, suggestions, ideas) about it is to be sent via
   e-mail to magnus.kempe@di.epfl.ch . Thanks.

   In all cases, the most up-to-date version of the FAQ is the version
   maintained on the Ada WWW Server. Please excuse any formatting
   inconsistencies in the posted version of this document, as it is
   automatically generated from the on-line version.

     _________________________________________________________________


3: Elementary Questions


3.1: How do I make operations directly visible without "use"ing the package?

   In Ada 83, you can rename the operations in your scope.

     -- Say you have an integer type called Int in package Types
     function "<" (Left, Right : Types.Int)
       return Boolean
       renames Types."<";
     -- Make sure the profiles of the first and last "<" match!


   For operators, Ada 95 introduces the "use type" clause:

     use type Types.Int; -- makes operators directly visible


3.2: How do I assign to an array of length 1?

   Because of ambiguity of parentheses, named notation must be used for
   one-element aggregates (or, under a different angle: a positional
   aggregate must have more than one component).

   See [RM9X 4.3.3(7)] as well as the syntax rule of
   positional_array_aggregate in [RM9X 4.3.3]; historians see [RM83
   4.3(4)].

     declare
       Array_of_One : array (1..1) of Float;
     begin
       -- Array_of_One := (10.0);   -- Won't work, parsed as an expression
                                    -- within parentheses

       Array_of_One := (1 => 10.0); -- No ambiguity here
     end;


   You can't write a one-element positional aggregate in Ada. Nor a
   zero-element aggregate. The reason for this restriction is that it
   would be difficult for compilers to determine whether:

     ( exp )

   is a parenthesized expression of some type, or an aggregate of an
   array type. If Ada had used some other notation for aggregates (say,
   "[...]"), then this problem would not exist.

   Apparently the original requirements for Ada forbade using certain
   ASCII characters, like '[' and ']', because those characters were not
   available on all hardware. Also, certain characters are used for
   different purposes and glyphs in countries that need additional
   letters not present in ASCII.


3.3: How do I create a C-style nul-terminated string?

   In a declaration block, append an ASCII.NUL to create a constant Ada
   string.

     declare
       Str_Nul : constant String := Str & ASCII.NUL;
     begin
       Call_Requiring_C_String (Str_Nul (Str_Nul'First)'Address);
     end;

-- or --

     function Nul_Terminate (Str : String)
       return String is
       Str_Nul : constant String := Str & ASCII.NUL;
     begin
       return Str_Nul;
     end Nul_Terminate;


3.4: How can I create an array of strings of various length?

   In Ada 83, you have to use string access types and "new" to get
   "ragged" arrays:

     type String_Access is
       access String;

     Strings : constant array (Positive range 1..3) of String_Access
             := ( 1 => new String'("One"),
                  2 => new String'("Two"),
                  3 => new String'("Three")
                );


   In Ada 95, the process is simplified by using aliased constants:

     type String_Access is
       access constant String;

     One : aliased constant String := "One";
     Two : aliased constant String := "Two";
     Three : aliased constant String := "Three";

     Strings : constant array (Positive range <>) of String_Access
             := ( 1 => One'Access,
                  2 => Two'Access,
                  3 => Three'Access
                );


3.5: I know an exception is raised, but my program quits with no warning. Why?


   On some Ada compilers, you have to manually "with" Text_IO before
   exception information is diplayed to the terminal.

   On other Ada compilers, you must set an environment variable flag in
   order to cause the exception information trace to be displayed.


3.6: I have only one task in my program, but it doesn't seem to run. Why?

   In Ada, the main procedure is automatically designated as a task.
   This task may be running forever, thus starving your other task(s),
   because pre-emptive scheduling is not required.

   If your Ada run-time exhibits that --pretty rare-- behavior, one
   solution is to explicitly put the main task to sleep within a loop
   construct, as in:

     procedure Main is
       task Test;
       task body Test is
       begin
         loop
           delay 1.0;
           Text_IO.Put_Line ("Test");
         end loop;
       end Test;
     begin
       loop
         delay 20.0;
         Text_IO.Put_Line ("Sleeping then writing");
       end loop;
     end Main;


3.7: How do I increase the stack size for a task?

   Define the task as a "task type" and then use a pragma representation
   clause.

     task type A_Task_Type;
     for A_Task_Type'STORAGE_SIZE use 10_000;
     -- 10K bytes allocated to instances of A_Task_Type
     A_Task : A_Task_Type;


3.8: What's the difference between a type conversion and a qualifier?

   Use a qualifier (tick) to tell the compiler what type you want; this
   incurs no run-time penalty. In other words, a qualifier "hints" the
   type.

     A : Integer := Integer'(1);  -- same as := 1
     B : Integer := Integer (1);  -- this is a conversion


3.9: How do I avoid the potential space in front of Integer'Image?

   Code a function that accepts a string and strips the leading blank.

     function Strip_Leading_Blank (Str : String)
       return String is
     begin -- Strip_Leading_Blank
       if Str (Str'First) = ' ' then
         return Str (1+Str'First .. Str'Last);
       else
         return Str;
       end if;
     end Strip_Leading_Blank;

     ...

     function My_Image (I : Integer)
       return String is
     begin -- My_Image
       return Strip_Leading_Blank (Integer'Image (I));
     end My_Image;

     ... My_Image (12) = "12" ...


3.10: Why is an exception raised when giving a default discriminant?

   Let's assume you would like to model varying-length strings:

     type V_String (Size : Natural := 0) is
       record
         S : String (1 .. Size);
       end record;


   (from Robert Dewar)

   When you give a default discriminant, then one method (I actually
   think it is the preferred method) of implementation is to allocate the
   maximum possible length. Since your discriminant is of type Natural,
   this clearly won't work!

   GNAT may compile it, but it won't run it, and indeed I consider it a
   GNAT bug (on the todo list) that no warning is issued at compile time
   for this misuse.

   Some compilers, notably Alsys and RR, have at least partially "solved"
   this problem by introducing hidden pointers, but this to me is an
   undesirable implementation choice.

   First, it means there is hidden heap activity, which seems
   undesirable. In a language where pointers are explicit, it is
   generally a good idea if allocation is also explicit, and certainly
   for real-time work, hidden anything is worrisome.

   Second, it is not easy to do uniformly. Alsys ends up introducing
   arbitrary restrictions on the composition of such types (try making an
   array of them), and RR introduces non-contiguous representations,
   which are legal but troublesome.

   To "solve" the problem yourself, just declare a reasonable maximum
   length, and use a subtype representing this length as the subtype of
   the discriminant:


     Max_Length : constant := 200;

     subtype Index is
       Natural range 0 .. Max_Length;

     type V_String (Size : Index := 0) is
       record
         S : String (1 .. Size);
       end record;


3.11: I am learning Ada. Can I experiment with a game program?

   Of course. The Public Ada library (FTP wuarchive.wustl.edu) has a
   portable Ada Tetris program in the languages/ada/misc/games directory.
   It uses tasking, keyboard input, and ANSI screen graphics. Have fun!

     _________________________________________________________________


4: Advantages of Ada


4.1: Why use Ada?

   Think of it like this: We're the kid on the street corner, licking
   that tasty ice cream cone on a hot summer day; an impish grin
   decorates our face as we consume our cool confection. Meanwhile, other
   kids gather round, noticing our pleasure. It matters not a whit that
   they've just had a drink, or had their fill with supper -- they now
   want ice cream. We offer no lecture on how good the ice cream is, we
   simply demonstrate that we are happy, and let their memories carry
   them to the nearest ice cream truck.

   (Sorry, I got a little carried away --DW).


4.2: Ada seems large and complex, why is it this way?

   (Robert Dewar, lead designer of the GNU Ada compiler, responds):

   During the Ada 9X development process we have often had fierce
   arguments over the need to simplify proposals, and I pointed out some
   time ago that the idea of simplicity is heavily overloaded:
     * simple to implement
     * simple to describe informally
     * simple to describe formally
     * results in simple programs
     * simple to understand and/or remember
     * short to describe


   None of these goals are quite the same, and often they severely
   conflict.

   If you listen to programming language design types, especially from
   universities, they often have very little experience in programming,
   and especially little experience in writing large delivered,
   maintained software. That doesn't mean they know nothing about
   programming languages, but it does tend to mean that their view of
   complexity is skewed, and in particularly concentrates on the
   simplicity of the language itself, rather than on the simplicity of
   resulting programs.

   A lot of the creative tension in the 9X design process arose from this
   same fundamental dichotomy. The design team tended to have a high
   tolerance for language complexity (partly because they were very good
   at understanding language details), but had a lot of experience in
   actual large scale programming, and so their idea of simplicity was
   biased heavily to simplifying Ada programs. The opposite voice,
   worried about the simplicity of the language itself, represented by a
   section of the DR's and ISO group (who, being a larger more diverse
   group tended to reflect a wider view), considered that the design team
   had gone too far in this direction. If you want to get a feel for the
   transitions, look at the early versions of the 9X ILS, particularly
   version 1.0.

   In retrospect, I think we came up with what is at least very close the
   optimal balance. Tuck can speak for himself here more clearly than I
   can speak for him, but I would guess that he and the other members of
   the team recognize that you have to be able to sell the resulting
   design as an acceptably simple whole, and thus must step back from the
   most extensive proposals, while on the other hand, the more
   conservative KISS sentiments were convinced to accept more features
   than they originally felt comfortable with because of convincing
   programming examples and discussions of resulting programming
   complexity. The third wing of opinion ("I don't care what you think,
   but if we can't implement it, then it's not much use!") was also
   effectively fed in from the user-implementor teams.

   Is the result too complex? Time will tell, but I think the balance is
   a successful blend.

   I am a little more worried about C++ since there is an even greater
   danger of kitchen sink mentality, fueled by lots of enthusiastic
   feature-hungry programmers, adding too much to C++. A lot of big new
   features have been added to C++ recently (notably templates and
   exceptions). It's not that these new features are useless (after all
   Ada programmers know the value of generics and exceptions!) but there
   is a tendency to always add things in a most-decorated form (the
   templates in C++ are much more general, but less safe, than Ada
   generics, and the exceptions in C++ are much more elaborate, but less
   efficient to implement, and trickier to understand). I think it will
   be important for the C++ guys to make a real effort to keep the
   continued development of the language under control to avoid going too
   far in the feature-rich direction.

     _________________________________________________________________


5: Object-Oriented Programming with Ada


5.1: Why does Ada have "tagged types" instead of classes?

   (Tucker Taft responds):

   Someone recently asked me to explain the difference between the
   meaning of the term "class" in C++ and its meaning in Ada 9X. Here is
   a synopsis of the answer:

   In C++, the term "class" refers to three different, but related
   things:
     * a language construct, that encapsulates the definitions of data
       members, member functions, nested types, etc.;

     * a particular kind of type, defined by a class construct (or by
       "struct" which is a special case of "class");

     * a set of types consisting of a type and all of its derivatives,
       direct and indirect.


   In Ada 9X, the term "class" refers only to the third of the above
   definitions. Ada 9X (and Ada 83) has three different terms for the
   concepts corresponding to the above three things:
     * a "package" encapsulates the definitions of types, objects,
       operations, exceptions, etc which are logically related. (The
       operations of a type defined immediately within the package where
       the type is declared are called, in 9X, the "primitive operations"
       of the type, and in some sense, define the "primitive" semantics
       of the type, especially if it is a private type.)

     * a "type" is characterized by a set of values and a set of
       primitive operations (there are a million definitions of "type,"
       unfortunately, but you know what I mean...);

     * a "class" is a set of types with similar values and operations; in
       particular, a type and and all of its derivatives, direct and
       indirect, represents a (derivation) class. Also, the set of
       integer types form the integer "class," and so on for the other
       language-defined classes of types in the language.


   Some OOP languages take an intermediary position. In CLOS, a "class"
   is not an encapsulating construct (CLOS has "packages"). However, a
   "class" is both a type and a set of types, depending on context.
   (Methods "float" freely.)

   The distinction Ada 9X makes between types and classes (= set of
   types) carries over into the semantic model, and allows some
   interesting capabilities not present in C++. In particular, in Ada 9X
   one can declare a "class-wide" object initialized by copy from a
   "class-wide" formal parameter, with the new object carrying over the
   underlying type of the actual parameter. For example:

     procedure Print_In_Bold (X : T'Class) is
       -- Copy X, make it bold face, and then print it.
       Copy_Of_X : T'Class := X;
     begin
        Make_Bold (Copy_Of_X);
        Print (Copy_Of_X);
     end P;


   In C++, when you declare an object, you must specify the "exact" class
   of the object -- it cannot be determined by the underlying class of
   the initializing value. Implementing the above procedure in a general
   way in C++ would be slightly more tedious.

   Similarly, in Ada 9X one can define an access type that designates
   only one specific type, or alternatively, one can define one that can
   designate objects of any type in a class (a "class-wide" access type).
   For example:

     type Fancy_Window_Ptr is access Fancy_Window;
       -- Only points at Fancy Windows -- no derivatives allowed
     type Any_Window_Ptr is access Window'Class;
       -- Points at Windows, and any derivatives thereof.


   In C++, all pointers/references are "class-wide" in this sense; you
   can't restrict them to point at only one "specific" type.

   In other words, C++ makes the distinction between "specific" and
   "class-wide" based on pointer/reference versus object/value, whereas
   in Ada 9X, this distinction is explicit, and corresponds to the
   distinction between "type" (one specific type) and "class" (set of
   types).

   The Ada 9X approach we believe (hope ;-) gives somewhat better control
   over static versus dynamic binding, and is less error prone since it
   is type-based, rather than being based on reference vs. value.

   In any case, in Ada 9X, C++, and CLOS it makes sense to talk about
   "class libraries," since a given library will generally consist of a
   set of interrelated types. In Ada 9X and CLOS, one could alternatively
   talk about a set of "reusable packages" and mean essentially the same
   thing.


5.2: Variant records seem like a dead feature now? When should I use them
instead of tagged types?

   Hmm, good question.


5.3: What is meant by "interface inheritance" and how does Ada support it?

   This answer intentionally left blank.


5.4: How do you do multiple inheritance in Ada 9X?

   There is a lengthy paper on the AJPO ftp server (ajpo.sei.cmu.edu)
   titled 9xm-inh.txt under /public/ada9x/docs(?)

   That document describes several mechanisms for achieving MI in Ada. It
   is not unusual, however, to find complaints about the syntax and the
   perceived burden it places on the developer. This is what Tucker Taft
   had to say when responging to such a criticism on comp.lang.ada:

   Coming up with a syntax for multiple inheritance was not the
   challenge. The challenge was coming up with a set of straightforward
   yet flexible rules for resolving the well known problems associated
   with multiple inheritance, namely:
     * If the same type appears as an ancestor more than once, should all
       or some of its data components be duplicated, or shared? If any
       are duplicated, how are they referenced unambiguously?

     * If the same-named (including same parameter/result profile)
       operation is inherited along two paths, how is the ambiguity
       resolved? Can you override each with different code? How do you
       refer to them later?

     * Etc...


   For answers, you can look at the various languages that define a
   built-in approach to multiple inheritance. Unfortunately, you will
   generally get a different answer for each language -- hardly a
   situation that suggests we will be able to craft an international
   consensus. Eiffel uses renaming and other techniques, which seem quite
   flexible, but at least in some examples, can be quite confusing (where
   you override "B" to change what "A" does in some distant ancestor).
   C++ has both non-virtual and virtual base clases, with a number of
   rules associated with each, and various limitations relating to
   downcasting and virtual base classes. CLOS uses simple name matching
   to control "slot" merging. Some languages require that all but one of
   the parent types be abstract, data-less types, so only interfaces are
   being inherited; however if the interfaces happen to collide, you
   still can end up with undesirable and potentially unresolvable
   collisions (where you really want different code for same-named
   interfaces inherited from different ancestors).

   One argument is that collisions are rare to begin with, so it doesn't
   make much different how they are resolved. That is probably true, but
   the argument doesn't work too well during an open language design
   process -- people get upset at the most unbelievably trivial and
   rarely uses features if not "correctly" designed (speaking from
   experience here ;-).

   Furthermore, given that many of the predominant uses of MI (separation
   of interface inheritance from implementation inheritance, gaining
   convenient access to another class's features, has-a relationships
   being coded using MI for convenience, etc.) are already handled very
   well in Ada 9X, it is hard to justify getting into the MI language
   design fray at all. As you nicely point out, the basic inheritance
   model in Ada 9X is simple and elegant. Why clutter it up with a lot of
   relatively ad-hoc rules to handle one particular approach to MI? For
   the rare cases where MI is really critical, the last thing the
   programmer wants in the language is the "wrong" MI approach built in.

   So the basic answer is that at this point in the evolution of OO
   language design, it seemed wiser to provide MI building blocks, rather
   than to foist the wrong approach on the programmer, and be regretting
   it and working around it for years to come.

   Perhaps [Douglas Arndt] said it best...

     Final note: inheritance is overrated, especially MI. ...


   If the only or primary type composition mechanism in the language is
   based on inheritance, then by all means, load it up. But Ada 9X
   provides several efficient and flexible type composition mechanisms,
   and there is no need to overburden inheritance with unnecessary and
   complicated baggage.


5.5: Why are Controlled types so, well, strange?

   (Tucker Taft responds):

   We considered many approaches to user-defined finalization and
   user-defined assignment. Ada presents challenges that make it harder
   to define assignment than in other languages, because assignment is
   used implicitly in several operations (by-copy parameter passing,
   function return, aggregates, object initialization, initialized
   allocators, etc.), and because Ada has types whose set of components
   can be changed as a result of an assignment.

   For example:

     type T (D : Boolean := False) is record
       case D is
         when False => null;
         when True => H : In_Hands;
       end case;
     end record;

     X,Z : T;
     Y : T := (True, H => ...);

     ...

     X := Y;   -- "X.H" component coming into existence
     Y := Z;   -- "Y.H" component going out of existence


   With a type like the one above, there are components that can come and
   go as a result of assignment. The most obvious definition of
   assignment would be:

     procedure ":="(Left : in out In_Hands; Right : in In_Hands);


   Unfortunately, this wouldn't work for the "H" component, because there
   is no preexisting "In_Hands" component to be assigned into in the
   first case, and in the second case, there is no "In_Hands" component
   to assign "from."

   Therefore, we decided to decompose the operation of assignment into
   separable pieces: finalization of the left hand side; simple copying
   of the data from the right hand side to the left hand side; and then
   adjustment of the new left hand side. Other decompositions are
   probably possible, but they generally suffer from not being easily
   composable, or not handling situations like the variant record above.

   You seem to be proposing a function named ":=" that presumably returns
   a copy of its in parameter. However, to do anything interesting it
   will have to copy the in parameter into a local variable, and then
   "fiddle" with than local variable (essentially what "Adjust" does),
   and then return that local variable (which will make yet another
   copy). The returned result will have to be put back into the desired
   place (which might make yet another copy). For a large object, this
   might involve several extra copies.

   By having the user write just that part of the operation that
   "fiddles" with the result after making a copy, we allow the
   implementation to eliminate redundant copying. Furthermore, some
   user-defined representations might be position dependent. That is, the
   final "fiddling" has to take place on the object in its final
   location. For example, one might want the object to point to itself.
   If the implementation copies an object after the user code has
   adjusted it, such self-references will no longer point to the right
   place.

   So, as usual, once one gets into working out the details and all the
   interactions, the "obvious" proposal (such as a procedure ":=") no
   longer looks like the best answer, and the best answer one can find
   potentially looks "clumsy" (at least before you try to work out the
   details of the alternatives).


5.6: What do "covariance" and "contravariance" mean, and does Ada support
either or both?

   (From Robert Martin) [This is C++ stuff, it must be completely
   re-written for Ada. --MK]


 R> covariance:  "changes with"
 R> contravariance: "changes against"

 R> class A
 R> {
 R>    public:
 R>      A* f(A*);   // method of class A, takes A argument and returns A
 R>      A* g(A*);   // same.
 R> };

 R> class B : public A // class B is a subclass of class A
 R> {
 R>   public:
 R>     B* f(B*);  // method of class B overrides f and is covariant.
 R>     A* g(A*);  // method of class B overrides g and is contravariant.
 R> };

 R> The function f is covariant because the type of its return value and
 R> argument changes with the class it belongs to.  The function g is
 R> contravariant because the types of its return value and arguments does not
 R> change with the class it belongs to.


   Actually, I would call g() invariant. If you look in Sather, (one of
   the principle languages with contravariance), you will see that the
   method in the decendent class actually can have aruments that are
   superclasses of the arguments of its parent. So for example:

class A : public ROOT
{
   public:
     A* f(A*);   // method of class A, takes A argument and returns A
     A* g(A*);   // same.
};

class B : public A // class B is a subclass of class A
{
  public:
    B* f(B*);  // method of class B overrides f and is covariant.
    ROOT* g(ROOT*);  // method of class B overrides g and is contravariant.
};


   To my knowledge the uses for contravariance are rare or nonexistent.
   (Anyone?). It just makes the rules easy for the compiler to type
   check. On the other hand, co-variance is extremely useful. Suppose you
   want to test for equality, or create a new object of the same type as
   the one in hand:

class A
{
   public:
      BOOLEAN equal(A*);
      A* create();
}

class B: public A
{
   public:
      BOOLEAN equal(B*);
      B* create();
}


   Here covariance is exactly what you want. Eiffel gives this to you,
   but the cost is giving up 100% compile time type safety. This seem
   necessary in cases like these.

   In fact, Eiffel gives you automatic ways to make a method covariant,
   called "anchored types". So you could declare, (in C++/eiffese):

class A
{
   public:
      BOOLEAN equal(like Current *);
      like Current * create();
}


   Which says equal takes an argument the same type as the current
   object, and create returns an object of the same type as current. Now,
   there is not even any need to redeclare these in class B. Those
   transformations happen for free!



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

* Ada FAQ: Programming with Ada (part 1 of 3)
@ 1995-03-21 18:10 Magnus Kempe
  0 siblings, 0 replies; 3+ messages in thread
From: Magnus Kempe @ 1995-03-21 18:10 UTC (permalink / raw)


Archive-name: computer-lang/Ada/programming/part1
Comp-lang-ada-archive-name: programming/part1
Posting-Frequency: monthly
Last-modified: 20 March 1995
Last-posted: 20 February 1995

                               Ada Programmer's
                      Frequently Asked Questions (FAQ)

   IMPORTANT NOTE: No FAQ can substitute for real teaching and
   documentation. There is an annotated list of Ada books in the
   companion comp.lang.ada FAQ.

This is part 1 of a 3-part posting.
Part 2 begins with question 5.6.
Part 3 begins with question 8.6.
They should be the next postings in this thread.


    Recent changes to this FAQ are listed in the first section after the table
    of contents.

Introduction

   Ada is an advanced, modern programming language, designed and
   standardized to support and strongly encourage widely recognized
   software engineering principles: reliability, portability, modularity,
   reusability, programming as a human activity, efficiency,
   maintainability, information hiding, abstract data types, genericity,
   concurrent programming, object-oriented programming, etc.

   All Ada compilers must pass a validation test. Ada is not a superset
   or extension of any other language. Ada does not allow the dangerous
   practices or effects of old languages, although it does provide
   standardized mechanisms to interface with other languages such as
   Fortran, Cobol, and C.

   Ada is recognized as an excellent vehicle for education in programming
   and software engineering, including for a first programming course.

   Ada is defined by an international standard (the language reference
   manual, or LRM), which has been revised in 1995. Ada is taught and
   used all around the world (not just in the USA). Ada is used in a very
   wide range of applications: banking, medical devices,
   telecommunications, air traffic control, airplanes, railroad
   signalling, satellites, rockets, etc.

   The latest version of this FAQ is always accessible through WWW as
   http://lglwww.epfl.ch/Ada/FAQ/programming.html

Maintenance

   This FAQ is maintained on an individual volunteer basis, by Magnus
   Kempe (Magnus.Kempe@di.epfl.ch). [Note: This is done as a hobby, not
   in my capacity as an employee at the Swiss Federal Institute of
   Technology. --MK]

   The coding style used in most of the example Ada code is my own, and
   you'll have to live with it (you may want to adopt it :-).


     _________________________________________________________________

   Opinions (if any) expressed are those of the submitters and/or
   maintainer.
     _________________________________________________________________

Table of Contents:

     * 1: Recent changes to the FAQ

     * 2: Information about this document

     * 3: Elementary Questions
          + 3.1: How do I make operations directly visible without
            "use"ing a package?
          + 3.2: How do I assign to an array of length 1?
          + 3.3: How do I create a C-style nul-terminated string?
          + 3.4: How can I create an array of strings of various length?
          + 3.5: I know an exception is raised, but my program quits with
            no warning. Why?
          + 3.6: I have only one task in my program, but it doesn't seem
            to run. Why?
          + 3.7: How do I increase the stack size for a task?
          + 3.8: What's the difference between a type conversion and a
            qualifier?
          + 3.9: How do I avoid the potential space in front of
            Integer'Image?
          + 3.10: Why is an exception raised when giving a default
            discriminant?
          + 3.11: When I want an Integer type, what's wrong with just
            using the predefined type Integer or Long_Integer? Why would
            I ever want to declare new Integer types?
          + 3.12: Since I can always declare my own portable integer
            types, why would I ever want to use the predefined type
            Integer?
          + 3.13: I am learning Ada. Can I experiment with a game
            program?


     * 4: Advantages of Ada
          + 4.1: Why use Ada?
          + 4.2: Ada seems large and complex, why is it this way?


     * 5: Object-Oriented Programming with Ada
          + 5.1: Why does Ada have "tagged types" instead of classes?
          + 5.2: Variant records seem like a dead feature now? When
            should I use them instead of tagged types?
          + 5.3: What is meant by "interface inheritance" and how does
            Ada support it?
          + 5.4: How do you do multiple inheritance in Ada 9X?
          + 5.5: Why are Controlled types so, well, strange?
          + 5.6: What do "covariance" and "contravariance" mean, and does
            Ada support either or both?
          + 5.7: What is meant by upcasting/expanding and
            downcasting/narrowing?
          + 5.8: How does Ada do "narrowing"?


     * 6: Ada Numerics
          + 6.1: Where can I find anonymous ftp sites for Ada math
            packages? In particular where are the random number
            generators?
          + 6.2: How can I write portable code in Ada 83 using predefined
            types like Float and Long_Float? Likewise, how can I write
            portable code that uses Math functions like Sin and Log that
            are defined for Float and Long_Float?
          + 6.3: Is Ada any good at numerics, and where can I learn more
            about it?
          + 6.4: How do I get Real valued and Complex valued math
            functions in Ada 95?
          + 6.5: What libraries or public algorithms exist for Ada?


     * 7: Efficiency of Ada Constructs
          + 7.1: How much extra overhead do generics have?


     * 8: Advanced Programming Techniques with Ada
          + 8.1: Does Ada have automatic constructors and destructors?
          + 8.2: How can I redefine assignment operations?
          + 8.3: Should I stick to a one package, one type approach while
            writing Ada software?
          + 8.4: What is the "Beaujolais Effect"?
          + 8.5: What about the "Ripple Effect"?
          + 8.6: How to write an Ada program to compute when one has had
            too much alcohol to legally drive?


     * 9: Ada and Other Programming Languages
          + 9.1: Where can I find programs that will translate from [some
            language] to Ada?
          + 9.2: How can I convert Ada 83 sources to Ada 9X?
          + 9.3: I hear that Ada is slower than Fortran or C, is that
            true?
          + 9.4: Isn't Ada less "elegant" than Eiffel?
          + 9.5: Are there any papers detailing the differences between
            Ada and C++?
          + 9.6: I keep hearing that Ada is a "strongly typed language",
            but it seems different from what's meant in C++. Are they
            different?
          + 9.7: I'm told Ada does all sorts of static type checking, but
            can't you get the same effect using a tool like "lint" with
            C?
          + 9.8: Does Ada have something like the Standard Template
            Library (STL) in C++, or like the components one finds in
            Smalltalk environments?
          + 9.9: Where can I find the equivalent of "printf" in Ada?


     * 10: Interfacing with Ada
          + 10.1: I am writing software that used the Distributed
            Interactive Simulation (DIS) interface, does an interface
            exist in Ada?
          + 10.2: Is there any support for Common Object Request Broker
            Architecture (CORBA) for Ada 9X?


     * 11: Finding Additional Information
          + 11.1: Where can I find Ada books?
          + 11.2: Are there other Ada-related FAQs?
          + 11.3: What is the "Ada WWW Server"?


     * 12: Pretty-printing and Measuring Ada Source Code
          + 12.1: Is there software that generates a pretty PostScript
            file from Ada source code?
          + 12.2: I use vgrind to do "pretty printing" of my source. Is
            there a vgrind definition for Ada?
          + 12.3: How about a source code reformatter?
          + 12.4: How can I count source lines of code (SLOC)?
          + 12.5: Can I measure other things?


     * 13: Common Confusions 
          + 13.1: Wasn't Ada designed by some committee? What kind of a
            language could you possibly get from a committee?
          + 13.2: I've heard the DoD is dropping all Military standards
            to reduce costs, doesn't that mean the mandate to use Ada
            goes away too?


     * 14: Credits

     * 15: Copying this FAQ


     _________________________________________________________________


1: Recent changes to the FAQ


     * 950320: more on DIS.
     * 950315: Ada is good at numerics, by Jonathan Parker.
     * 950309: drinking and driving example.
     * 950306: added counting SLOC and measuring.
     * 950222: fixed some typos.
     * 950207: revised introduction.
     * 950202: updated discussion of C++ STL and Smalltalk library.
     * 950126: advantages of code sharing for generics; Pascal to Ada
       tool.
     * 950125: why define new integer types, and why use the predefined
       Integer type, submitted by Jonathan Parker.
     * 950124: approved for posting in *.answers.
     * 950116: converting Ada 83 code to Ada 9X.
     * 950106: lengthy code sections extracted and put on FTP server.
     * 950105: printf solution; update on exception traces and vgrind.
     * 950104: links from TOC to all questions.
     * 941222: pretty-printing solutions.
     * 941220: numbered all questions; various updates.
     * 941215: one-element aggregates.
     * 941212: additional elementary questions, submitted by Paul Pukite.
     * 941212: examining the so-called "elegance" of Eiffel (vs. Ada), by
       Tucker Taft.
     * 941212: integrated "Ada Numerics" section contributed by Jonathan
       Parker.
     * 941208: added default discriminant and "Beaujolais effect."
     * December 1994: more re-organization, HTML'ized.
     * October 1994: FAQ format by Magnus Kempe.
     * September 1994: first draft by Dave Weller.


   What's important and missing:
     * everything, life, and 42


     _________________________________________________________________


2: Information about this document

   This file is posted monthly to comp.lang.ada, comp.answers, and
   news.answers.

   This document has a home on the Ada WWW Server, in hypertext format,
   URL http://lglwww.epfl.ch/Ada/FAQ/programming.html

   It is available --as posted in *.answers-- on rtfm.mit.edu, which
   archives all FAQ files posted to *.answers; see
   ftp://rtfm.mit.edu/pub/usenet-by-group/news.answers/computer-lang/Ada

   The text-only version is also available in directory
   ftp://lglftp.epfl.ch/pub/Ada/FAQ

   Magnus Kempe maintains this document; it's a hobby, not a job.
   Feedback (corrections, suggestions, ideas) about it is to be sent via
   e-mail to magnus.kempe@di.epfl.ch
   Thanks.

   In all cases, the most up-to-date version of the FAQ is the version
   maintained on the Ada WWW Server. Please excuse any formatting
   inconsistencies in the posted version of this document, as it is
   automatically generated from the on-line version.

     _________________________________________________________________


3: Elementary Questions


3.1: How do I make operations directly visible without "use"ing the package?

   In Ada 83, you can rename the operations in your scope.

     -- Say you have an integer type called Int in package Types
     function "<" (Left, Right : Types.Int)
       return Boolean
       renames Types."<";
     -- Make sure the profiles of the first and last "<" match!


   For operators, Ada 95 introduces the "use type" clause:

     use type Types.Int; -- makes operators directly visible


3.2: How do I assign to an array of length 1?

   Because of ambiguity of parentheses, named notation must be used for
   one-element aggregates (or, under a different angle: a positional
   aggregate must have more than one component).

   See [RM9X 4.3.3(7)] as well as the syntax rule of
   positional_array_aggregate in [RM9X 4.3.3]; historians see [RM83
   4.3(4)].

     declare
       Array_of_One : array (1..1) of Float;
     begin
       -- Array_of_One := (10.0);   -- Won't work, parsed as an expression
                                    -- within parentheses

       Array_of_One := (1 => 10.0); -- No ambiguity here
     end;


   You can't write a one-element positional aggregate in Ada. Nor a
   zero-element aggregate. The reason for this restriction is that it
   would be difficult for compilers to determine whether:

     ( exp )

   is a parenthesized expression of some type, or an aggregate of an
   array type. If Ada had used some other notation for aggregates (say,
   "[...]"), then this problem would not exist.

   Apparently the original requirements for Ada forbade using certain
   ASCII characters, like '[' and ']', because those characters were not
   available on all hardware. Also, certain characters are used for
   different purposes and glyphs in countries that need additional
   letters not present in ASCII.


3.3: How do I create a C-style nul-terminated string?

   In a declaration block, append an ASCII.NUL to create a constant Ada
   string.

     declare
       Str_Nul : constant String := Str & ASCII.NUL;
     begin
       Call_Requiring_C_String (Str_Nul (Str_Nul'First)'Address);
     end;

-- or --

     function Nul_Terminate (Str : String)
       return String is
       Str_Nul : constant String := Str & ASCII.NUL;
     begin
       return Str_Nul;
     end Nul_Terminate;


3.4: How can I create an array of strings of various length?

   In Ada 83, you have to use string access types and "new" to get
   "ragged" arrays:

     type String_Access is
       access String;

     Strings : constant array (Positive range 1..3) of String_Access
             := ( 1 => new String'("One"),
                  2 => new String'("Two"),
                  3 => new String'("Three")
                );


   In Ada 95, the process is simplified by using aliased constants:

     type String_Access is
       access constant String;

     One : aliased constant String := "One";
     Two : aliased constant String := "Two";
     Three : aliased constant String := "Three";

     Strings : constant array (Positive range <>) of String_Access
             := ( 1 => One'Access,
                  2 => Two'Access,
                  3 => Three'Access
                );


3.5: I know an exception is raised, but my program quits with no warning. Why?


   On some Ada compilers, you have to manually "with" Text_IO before
   exception information is diplayed to the terminal.

   On other Ada compilers, you must set an environment variable flag in
   order to cause the exception information trace to be displayed.


3.6: I have only one task in my program, but it doesn't seem to run. Why?

   In Ada, the main procedure is automatically designated as a task.
   This task may be running forever, thus starving your other task(s),
   because pre-emptive scheduling is not required.

   If your Ada run-time exhibits that --pretty rare-- behavior, one
   solution is to explicitly put the main task to sleep within a loop
   construct, as in:

     procedure Main is
       task Test;
       task body Test is
       begin
         loop
           delay 1.0;
           Text_IO.Put_Line ("Test");
         end loop;
       end Test;
     begin
       loop
         delay 20.0;
         Text_IO.Put_Line ("Sleeping then writing");
       end loop;
     end Main;


3.7: How do I increase the stack size for a task?

   Define the task as a "task type" and then use a pragma representation
   clause.

     task type A_Task_Type;
     for A_Task_Type'STORAGE_SIZE use 10_000;
     -- 10K bytes allocated to instances of A_Task_Type
     A_Task : A_Task_Type;


3.8: What's the difference between a type conversion and a qualifier?

   Use a qualifier (tick) to tell the compiler what type you want; this
   incurs no run-time penalty. In other words, a qualifier "hints" the
   type.

     A : Integer := Integer'(1);  -- same as := 1
     B : Integer := Integer (1);  -- this is a conversion


3.9: How do I avoid the potential space in front of Integer'Image?

   Code a function that accepts a string and strips the leading blank.

     function Strip_Leading_Blank (Str : String)
       return String is
     begin -- Strip_Leading_Blank
       if Str (Str'First) = ' ' then
         return Str (1+Str'First .. Str'Last);
       else
         return Str;
       end if;
     end Strip_Leading_Blank;

     ...

     function My_Image (I : Integer)
       return String is
     begin -- My_Image
       return Strip_Leading_Blank (Integer'Image (I));
     end My_Image;

     ... My_Image (12) = "12" ...


3.10: Why is an exception raised when giving a default discriminant?

   Let's assume you would like to model varying-length strings:

     type V_String (Size : Natural := 0) is
       record
         S : String (1 .. Size);
       end record;


   (from Robert Dewar)

   When you give a default discriminant, then one method (I actually
   think it is the preferred method) of implementation is to allocate the
   maximum possible length. Since your discriminant is of type Natural,
   this clearly won't work!

   GNAT may compile it, but it won't run it, and indeed I consider it a
   GNAT bug (on the todo list) that no warning is issued at compile time
   for this misuse.

   Some compilers, notably Alsys and RR, have at least partially "solved"
   this problem by introducing hidden pointers, but this to me is an
   undesirable implementation choice.

   First, it means there is hidden heap activity, which seems
   undesirable. In a language where pointers are explicit, it is
   generally a good idea if allocation is also explicit, and certainly
   for real-time work, hidden anything is worrisome.

   Second, it is not easy to do uniformly. Alsys ends up introducing
   arbitrary restrictions on the composition of such types (try making an
   array of them), and RR introduces non-contiguous representations,
   which are legal but troublesome.

   To "solve" the problem yourself, just declare a reasonable maximum
   length, and use a subtype representing this length as the subtype of
   the discriminant:

     Max_Length : constant := 200;

     subtype Index is
       Natural range 0 .. Max_Length;

     type V_String (Size : Index := 0) is
       record
         S : String (1 .. Size);
       end record;


3.11: When I want an Integer type, what's wrong with just using the predefined
type Integer or Long_Integer? Why would I ever want to declare new Integer
types?

   If you declare 2 distinct integer types, for example,

     type Data_Index        is range 1..100;
     type Time_Series_Index is range 0..2**15-1;


   then objects of type Data_Index can't be assigned (directly) to
   variables of type Time_Series_Index, and vice-versa. Likewise,
   variables of these 2 types can't be mixed in arithmetical expressions
   (without explicit type conversions). This may seem like a source of
   endless irritation, but on the contrary, good progammers use it to
   improve the clarity of their code, to make it more robust, and more
   portable. The first 2 examples discuss this. The third example
   discusses the declaration of machine-portable 32-bit integers.
   Declaring objects of type Integer can be highly non-portable, and of
   course type Long_Integer may not exist on some compilers.

    Example 1.

   Suppose you declare arrays using the above indices:

     type Time_Series is array (Time_Series_Index) of Float;
     type Y_Axis_Data is array (Data_Index)        of Float;

     Measurement : Time_Series;


   Now if you mistakenly try to iterate over one array with the index of
   the other, the compiler can catch the error at compile time:

     for I in Data_Index loop
        Sum := Sum + Measurement(I);  -- compilation error
     end loop;


    Example 2.

   This is lifted from Tucker Taft's brief introduction to Ada 95 in the
   contributed papers section of the Ada World Wide Web homepage. Here
   Tucker uses the Ada 95 unsigned integers, called modular types, in the
   implementation of a protected type, which defines a disk control unit.
   Modular types are integer types with "and", "or" and "xor" defined, so
   systems programmers are likely to use them as bit masks. Just as the
   array indices of the 2 arrays defined above are never meant to be
   mixed, the modular integer types used to implement the disk control
   unit are never meant to be mixed. To make sure the compiler enforces
   this, they are declared as distinct types:

     type Flags   is mod 2**4;  -- a 4-bit flags field
     type Control is mod 2**4;  -- A 4-bit control field

     Status_Mask  : constant Flags := 2#1001#;   -- Set first and last bits.
     Status_Ready : constant Flags := 2#1000#;   -- Status = Ready

     Start_Xfr    : constant Control := 2#0001#; -- Initiate xfr command


   Now if someone attempts to apply a Flag variable where a Control
   variable should be used (or vice-versa) the compiler will catch the
   error. This is especially important when the code is maintained by
   programmers who did not write it.

    Remarks on Examples 1 and 2.

   1. Notice that in both examples the programmer was able to state his
   intentions rather forcefully in the code - intentions that otherwise
   might have been expressed much less forcefully in comment statements.
   Because of Ada's strong typing model, the compiler was able to catch
   errors at compile-time when the programmer's intentions were violated.


   2. Notice also that the Integer declarations in the 2 examples are
   machine portable, unlike Integer and Long_Integer. A compiler will
   typically map these integer types onto the most efficient base type
   that is available on the target machine.

    Example 3.

   Although the examples given above are good ones, it is not necessarily
   a common practice to define a large number of distinct integer types.
   In many cases it is appropriate to use (say) a 32-bit integer (or a
   small number of such types) and declare appropriate subtypes of it
   (them). To declare a portable 32-bit integer (or more accurately, the
   most efficient integer that is at least 32-bits):

   type Int_tmp is range -2**31+1 .. 2**31-1;
   type Integer_32 is range Int_tmp'Base'First..Int_tmp'Base'Last;


   A compiler may reject this declaration if no suitable base type is
   available, but this is rare. What happens is this: in order to
   implement Int_tmp, the compiler chooses as the base type of Int_tmp an
   integer type that is available on the target machine. This base type
   is usually the most efficient integer that accomodates the range of
   Int_tmp, which in turn is usually the machine's 32-bit integer. (It
   might even be a 64-bit integer on some machines, in which case
   Integer_32'Size = 64, and Integer_32'Last = 2**63-1. Maybe we should
   not call it Integer_32!)


3.12: Since I can always declare my own portable integer types, why would I
ever want to use the predefined type Integer?

   The language itself provides some guidance here. The predefined type
   Integer is used by Ada in the implementation of a number of convenient
   services. The following examples describe some of these services.
   Notice that in most of the following examples, it is unlikely that it
   will ever matter whether or not the predefined type Integer is
   16-bits, 32-bits, 48-bits, or 64-bits.

   a) The exponentiation of X (written X**N) is defined by the language
   for any floating point or integer X, provided N is of type Integer. (N
   should be non-negative for integer X though.)

   b) Ada's predefined String type (really just a packed unconstrained
   array of characters) uses an index of subtype Positive (i.e. type
   Integer).

   c) The array index in the following "short-hand" array declaration is
   implicitly defined to be type Integer:

     A : array(10..40) of Float;


   d) The loop parameter I in the following for loop is implicitly
   declared type Integer:

     for I in 10..40 loop
      ...
     end loop;


   This application of type Integer is the one most likely to get you
   into portability trouble. If you write: "for I in 1..2**17 loop", then
   you get a constraint error on compilers that make Integer 16-bits,
   because 2**17 is out of range of any Ada 16-bit integer.


3.13: I am learning Ada. Can I experiment with a game program?

   Of course. The Public Ada library (FTP wuarchive.wustl.edu) has a
   portable Ada Tetris program in the languages/ada/misc/games directory.
   It uses tasking, keyboard input, and ANSI screen graphics. Have fun!

     _________________________________________________________________


4: Advantages of Ada


4.1: Why use Ada?

   Think of it like this: We're the kid on the street corner, licking
   that tasty ice cream cone on a hot summer day; an impish grin
   decorates our face as we consume our cool confection. Meanwhile, other
   kids gather round, noticing our pleasure. It matters not a whit that
   they've just had a drink, or had their fill with supper -- they now
   want ice cream. We offer no lecture on how good the ice cream is, we
   simply demonstrate that we are happy, and let their memories carry
   them to the nearest ice cream truck.

   (Sorry, I got a little carried away --DW).


4.2: Ada seems large and complex, why is it this way?

   (Robert Dewar, lead designer of the GNU Ada compiler, responds):

   During the Ada 9X development process we have often had fierce
   arguments over the need to simplify proposals, and I pointed out some
   time ago that the idea of simplicity is heavily overloaded:
     * simple to implement
     * simple to describe informally
     * simple to describe formally
     * results in simple programs
     * simple to understand and/or remember
     * short to describe


   None of these goals are quite the same, and often they severely
   conflict.

   If you listen to programming language design types, especially from
   universities, they often have very little experience in programming,
   and especially little experience in writing large delivered,
   maintained software. That doesn't mean they know nothing about
   programming languages, but it does tend to mean that their view of
   complexity is skewed, and in particularly concentrates on the
   simplicity of the language itself, rather than on the simplicity of
   resulting programs.

   A lot of the creative tension in the 9X design process arose from this
   same fundamental dichotomy. The design team tended to have a high
   tolerance for language complexity (partly because they were very good
   at understanding language details), but had a lot of experience in
   actual large scale programming, and so their idea of simplicity was
   biased heavily to simplifying Ada programs. The opposite voice,
   worried about the simplicity of the language itself, represented by a
   section of the DR's and ISO group (who, being a larger more diverse
   group tended to reflect a wider view), considered that the design team
   had gone too far in this direction. If you want to get a feel for the
   transitions, look at the early versions of the 9X ILS, particularly
   version 1.0.

   In retrospect, I think we came up with what is at least very close the
   optimal balance. Tuck can speak for himself here more clearly than I
   can speak for him, but I would guess that he and the other members of
   the team recognize that you have to be able to sell the resulting
   design as an acceptably simple whole, and thus must step back from the
   most extensive proposals, while on the other hand, the more
   conservative KISS sentiments were convinced to accept more features
   than they originally felt comfortable with because of convincing
   programming examples and discussions of resulting programming
   complexity. The third wing of opinion ("I don't care what you think,
   but if we can't implement it, then it's not much use!") was also
   effectively fed in from the user-implementor teams.

   Is the result too complex? Time will tell, but I think the balance is
   a successful blend.

   I am a little more worried about C++ since there is an even greater
   danger of kitchen sink mentality, fueled by lots of enthusiastic
   feature-hungry programmers, adding too much to C++. A lot of big new
   features have been added to C++ recently (notably templates and
   exceptions). It's not that these new features are useless (after all
   Ada programmers know the value of generics and exceptions!) but there
   is a tendency to always add things in a most-decorated form (the
   templates in C++ are much more general, but less safe, than Ada
   generics, and the exceptions in C++ are much more elaborate, but less
   efficient to implement, and trickier to understand). I think it will
   be important for the C++ guys to make a real effort to keep the
   continued development of the language under control to avoid going too
   far in the feature-rich direction.

     _________________________________________________________________


5: Object-Oriented Programming with Ada


5.1: Why does Ada have "tagged types" instead of classes?

   (Tucker Taft responds):

   Someone recently asked me to explain the difference between the
   meaning of the term "class" in C++ and its meaning in Ada 9X. Here is
   a synopsis of the answer:

   In C++, the term "class" refers to three different, but related
   things:
     * a language construct, that encapsulates the definitions of data
       members, member functions, nested types, etc.;

     * a particular kind of type, defined by a class construct (or by
       "struct" which is a special case of "class");

     * a set of types consisting of a type and all of its derivatives,
       direct and indirect.


   In Ada 9X, the term "class" refers only to the third of the above
   definitions. Ada 9X (and Ada 83) has three different terms for the
   concepts corresponding to the above three things:
     * a "package" encapsulates the definitions of types, objects,
       operations, exceptions, etc which are logically related. (The
       operations of a type defined immediately within the package where
       the type is declared are called, in 9X, the "primitive operations"
       of the type, and in some sense, define the "primitive" semantics
       of the type, especially if it is a private type.)

     * a "type" is characterized by a set of values and a set of
       primitive operations (there are a million definitions of "type,"
       unfortunately, but you know what I mean...);

     * a "class" is a set of types with similar values and operations; in
       particular, a type and and all of its derivatives, direct and
       indirect, represents a (derivation) class. Also, the set of
       integer types form the integer "class," and so on for the other
       language-defined classes of types in the language.


   Some OOP languages take an intermediary position. In CLOS, a "class"
   is not an encapsulating construct (CLOS has "packages"). However, a
   "class" is both a type and a set of types, depending on context.
   (Methods "float" freely.)

   The distinction Ada 9X makes between types and classes (= set of
   types) carries over into the semantic model, and allows some
   interesting capabilities not present in C++. In particular, in Ada 9X
   one can declare a "class-wide" object initialized by copy from a
   "class-wide" formal parameter, with the new object carrying over the
   underlying type of the actual parameter. For example:

     procedure Print_In_Bold (X : T'Class) is
       -- Copy X, make it bold face, and then print it.
       Copy_Of_X : T'Class := X;
     begin
        Make_Bold (Copy_Of_X);
        Print (Copy_Of_X);
     end P;


   In C++, when you declare an object, you must specify the "exact" class
   of the object -- it cannot be determined by the underlying class of
   the initializing value. Implementing the above procedure in a general
   way in C++ would be slightly more tedious.

   Similarly, in Ada 9X one can define an access type that designates
   only one specific type, or alternatively, one can define one that can
   designate objects of any type in a class (a "class-wide" access type).
   For example:

     type Fancy_Window_Ptr is access Fancy_Window;
       -- Only points at Fancy Windows -- no derivatives allowed
     type Any_Window_Ptr is access Window'Class;
       -- Points at Windows, and any derivatives thereof.


   In C++, all pointers/references are "class-wide" in this sense; you
   can't restrict them to point at only one "specific" type.

   In other words, C++ makes the distinction between "specific" and
   "class-wide" based on pointer/reference versus object/value, whereas
   in Ada 9X, this distinction is explicit, and corresponds to the
   distinction between "type" (one specific type) and "class" (set of
   types).

   The Ada 9X approach we believe (hope ;-) gives somewhat better control
   over static versus dynamic binding, and is less error prone since it
   is type-based, rather than being based on reference vs. value.

   In any case, in Ada 9X, C++, and CLOS it makes sense to talk about
   "class libraries," since a given library will generally consist of a
   set of interrelated types. In Ada 9X and CLOS, one could alternatively
   talk about a set of "reusable packages" and mean essentially the same
   thing.


5.2: Variant records seem like a dead feature now? When should I use them
instead of tagged types?

   Hmm, good question.


5.3: What is meant by "interface inheritance" and how does Ada support it?

   This answer intentionally left blank.


5.4: How do you do multiple inheritance in Ada 9X?

   There is a lengthy paper in file
   ftp://sw-eng.falls-church.va.us/public/AdaIC/docs/9Xproject/9xm-inh.txt

   That document describes several mechanisms for achieving MI in Ada. It
   is not unusual, however, to find complaints about the syntax and the
   perceived burden it places on the developer. This is what Tucker Taft
   had to say when responging to such a criticism on comp.lang.ada:

   Coming up with a syntax for multiple inheritance was not the
   challenge. The challenge was coming up with a set of straightforward
   yet flexible rules for resolving the well known problems associated
   with multiple inheritance, namely:
     * If the same type appears as an ancestor more than once, should all
       or some of its data components be duplicated, or shared? If any
       are duplicated, how are they referenced unambiguously?

     * If the same-named (including same parameter/result profile)
       operation is inherited along two paths, how is the ambiguity
       resolved? Can you override each with different code? How do you
       refer to them later?

     * Etc...


   For answers, you can look at the various languages that define a
   built-in approach to multiple inheritance. Unfortunately, you will
   generally get a different answer for each language -- hardly a
   situation that suggests we will be able to craft an international
   consensus. Eiffel uses renaming and other techniques, which seem quite
   flexible, but at least in some examples, can be quite confusing (where
   you override "B" to change what "A" does in some distant ancestor).
   C++ has both non-virtual and virtual base clases, with a number of
   rules associated with each, and various limitations relating to
   downcasting and virtual base classes. CLOS uses simple name matching
   to control "slot" merging. Some languages require that all but one of
   the parent types be abstract, data-less types, so only interfaces are
   being inherited; however if the interfaces happen to collide, you
   still can end up with undesirable and potentially unresolvable
   collisions (where you really want different code for same-named
   interfaces inherited from different ancestors).

   One argument is that collisions are rare to begin with, so it doesn't
   make much different how they are resolved. That is probably true, but
   the argument doesn't work too well during an open language design
   process -- people get upset at the most unbelievably trivial and
   rarely uses features if not "correctly" designed (speaking from
   experience here ;-).

   Furthermore, given that many of the predominant uses of MI (separation
   of interface inheritance from implementation inheritance, gaining
   convenient access to another class's features, has-a relationships
   being coded using MI for convenience, etc.) are already handled very
   well in Ada 9X, it is hard to justify getting into the MI language
   design fray at all. As you nicely point out, the basic inheritance
   model in Ada 9X is simple and elegant. Why clutter it up with a lot of
   relatively ad-hoc rules to handle one particular approach to MI? For
   the rare cases where MI is really critical, the last thing the
   programmer wants in the language is the "wrong" MI approach built in.

   So the basic answer is that at this point in the evolution of OO
   language design, it seemed wiser to provide MI building blocks, rather
   than to foist the wrong approach on the programmer, and be regretting
   it and working around it for years to come.

   Perhaps [Douglas Arndt] said it best...

     Final note: inheritance is overrated, especially MI. ...


   If the only or primary type composition mechanism in the language is
   based on inheritance, then by all means, load it up. But Ada 9X
   provides several efficient and flexible type composition mechanisms,
   and there is no need to overburden inheritance with unnecessary and
   complicated baggage.


5.5: Why are Controlled types so, well, strange?

   (Tucker Taft responds):

   We considered many approaches to user-defined finalization and
   user-defined assignment. Ada presents challenges that make it harder
   to define assignment than in other languages, because assignment is
   used implicitly in several operations (by-copy parameter passing,
   function return, aggregates, object initialization, initialized
   allocators, etc.), and because Ada has types whose set of components
   can be changed as a result of an assignment.

   For example:

     type T (D : Boolean := False) is record
       case D is
         when False => null;
         when True => H : In_Hands;
       end case;
     end record;

     X,Z : T;
     Y : T := (True, H => ...);

     ...

     X := Y;   -- "X.H" component coming into existence
     Y := Z;   -- "Y.H" component going out of existence


   With a type like the one above, there are components that can come and
   go as a result of assignment. The most obvious definition of
   assignment would be:

     procedure ":="(Left : in out In_Hands; Right : in In_Hands);


   Unfortunately, this wouldn't work for the "H" component, because there
   is no preexisting "In_Hands" component to be assigned into in the
   first case, and in the second case, there is no "In_Hands" component
   to assign "from."

   Therefore, we decided to decompose the operation of assignment into
   separable pieces: finalization of the left hand side; simple copying
   of the data from the right hand side to the left hand side; and then
   adjustment of the new left hand side. Other decompositions are
   probably possible, but they generally suffer from not being easily
   composable, or not handling situations like the variant record above.

   You seem to be proposing a function named ":=" that presumably returns
   a copy of its in parameter. However, to do anything interesting it
   will have to copy the in parameter into a local variable, and then
   "fiddle" with than local variable (essentially what "Adjust" does),
   and then return that local variable (which will make yet another
   copy). The returned result will have to be put back into the desired
   place (which might make yet another copy). For a large object, this
   might involve several extra copies.

   By having the user write just that part of the operation that
   "fiddles" with the result after making a copy, we allow the
   implementation to eliminate redundant copying. Furthermore, some
   user-defined representations might be position dependent. That is, the
   final "fiddling" has to take place on the object in its final
   location. For example, one might want the object to point to itself.
   If the implementation copies an object after the user code has
   adjusted it, such self-references will no longer point to the right
   place.

   So, as usual, once one gets into working out the details and all the
   interactions, the "obvious" proposal (such as a procedure ":=") no
   longer looks like the best answer, and the best answer one can find
   potentially looks "clumsy" (at least before you try to work out the
   details of the alternatives).



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

* Ada FAQ: Programming with Ada (part 1 of 3)
@ 1995-04-20  0:00 Magnus Kempe
  0 siblings, 0 replies; 3+ messages in thread
From: Magnus Kempe @ 1995-04-20  0:00 UTC (permalink / raw)


Archive-name: computer-lang/Ada/programming/part1
Comp-lang-ada-archive-name: programming/part1
Posting-Frequency: monthly
Last-modified: 20 April 1995
Last-posted: 21 March 1995

                               Ada Programmer's
                      Frequently Asked Questions (FAQ)

   IMPORTANT NOTE: No FAQ can substitute for real teaching and
   documentation. There is an annotated list of Ada books in the
   companion comp.lang.ada FAQ.

    Recent changes to this FAQ are listed in the first section after the table
    of contents. This document is under explicit copyright.

This is part 1 of a 3-part posting.
Part 2 begins with question 5.6.
Part 3 begins with question 8.5.
They should be the next postings in this thread.


Introduction

   Ada is an advanced, modern programming language, designed and
   standardized to support and strongly encourage widely recognized
   software engineering principles: reliability, portability, modularity,
   reusability, programming as a human activity, efficiency,
   maintainability, information hiding, abstract data types, genericity,
   concurrent programming, object-oriented programming, etc.

   All validated Ada compilers (i.e. a huge majority of the commercial
   Ada compilers) have passed a controlled validation process using an
   extensive validation suite. Ada is not a superset or extension of any
   other language. Ada does not allow the dangerous practices or effects
   of old languages, although it does provide standardized mechanisms to
   interface with other languages such as Fortran, Cobol, and C.

   Ada is recognized as an excellent vehicle for education in programming
   and software engineering, including for a first programming course.

   Ada is defined by an international standard (the language reference
   manual, or LRM), which has been revised in 1995. Ada is taught and
   used all around the world (not just in the USA). Ada is used in a very
   wide range of applications: banking, medical devices,
   telecommunications, air traffic control, airplanes, railroad
   signalling, satellites, rockets, etc.

   The latest version of this FAQ is always accessible through WWW as
   http://lglwww.epfl.ch/Ada/FAQ/programming.html

Maintenance

   This FAQ is maintained on an individual volunteer basis, by Magnus
   Kempe (Magnus.Kempe@di.epfl.ch). [Note: This is done as a hobby, not
   in my capacity as an employee at the Swiss Federal Institute of
   Technology. --MK]

   The coding style used in most of the example Ada code is my own, and
   you'll have to live with it (you may want to adopt it :-).


     _________________________________________________________________

   Opinions (if any) expressed are those of the submitters and/or
   maintainer.
     _________________________________________________________________

Table of Contents:

     * 1: Recent changes to this FAQ

     * 2: Information about this document

     * 3: Elementary Questions
          + 3.1: How do I make operations directly visible without
            "use"ing a package?
          + 3.2: How do I assign to an array of length 1?
          + 3.3: How do I create a C-style nul-terminated string?
          + 3.4: How can I create an array of strings of various length?
          + 3.5: I know an exception is raised, but my program quits with
            no warning. Why?
          + 3.6: I have only one task in my program, but it doesn't seem
            to run. Why?
          + 3.7: How do I increase the stack size for a task?
          + 3.8: What's the difference between a type conversion and a
            qualifier?
          + 3.9: How do I avoid the potential space in front of
            Integer'Image?
          + 3.10: Why is an exception raised when giving a default
            discriminant?
          + 3.11: When I want an Integer type, what's wrong with just
            using the predefined type Integer or Long_Integer? Why would
            I ever want to declare new Integer types?
          + 3.12: Since I can always declare my own portable integer
            types, why would I ever want to use the predefined type
            Integer?
          + 3.13: I am learning Ada. Can I experiment with a game
            program?


     * 4: Advantages of Ada
          + 4.1: Why use Ada?
          + 4.2: Ada seems large and complex, why is it this way?


     * 5: Object-Oriented Programming with Ada
          + 5.1: Why does Ada have "tagged types" instead of classes?
          + 5.2: Variant records seem like a dead feature now. When
            should I use them instead of tagged types?
          + 5.3: What is meant by "interface inheritance" and how does
            Ada support it?
          + 5.4: How do you do multiple inheritance in Ada 9X?
          + 5.5: Why are Controlled types so, well, strange?
          + 5.6: What do "covariance" and "contravariance" mean, and does
            Ada support either or both?
          + 5.7: What is meant by upcasting/expanding and
            downcasting/narrowing?
          + 5.8: How does Ada do "narrowing"?


     * 6: Ada Numerics
          + 6.1: Where can I find anonymous ftp sites for Ada math
            packages? In particular where are the random number
            generators?
          + 6.2: How can I write portable code in Ada 83 using predefined
            types like Float and Long_Float? Likewise, how can I write
            portable code that uses Math functions like Sin and Log that
            are defined for Float and Long_Float?
          + 6.3: Is Ada any good at numerics, and where can I learn more
            about it?
          + 6.4: How do I get Real valued and Complex valued math
            functions in Ada 95?
          + 6.5: What libraries or public algorithms exist for Ada?


     * 7: Efficiency of Ada Constructs
          + 7.1: How much extra overhead do generics have?


     * 8: Advanced Programming Techniques with Ada
          + 8.1: Does Ada have automatic constructors and destructors?
          + 8.2: How can I redefine assignment operations?
          + 8.3: Should I stick to a one package, one type approach while
            writing Ada software?
          + 8.4: What is the "Beaujolais Effect"?
          + 8.5: What about the "Ripple Effect"?
          + 8.6: How to write an Ada program to compute when one has had
            too much alcohol to legally drive?
          + 8.7: Does Ada have macros?


     * 9: Ada and Other Programming Languages
          + 9.1: Where can I find programs that will translate from [some
            language] to Ada?
          + 9.2: How can I convert Ada 83 sources to Ada 9X?
          + 9.3: I hear that Ada is slower than Fortran or C, is that
            true?
          + 9.4: Isn't Ada less "elegant" than Eiffel?
          + 9.5: Are there any papers detailing the differences between
            Ada and C++?
          + 9.6: I keep hearing that Ada is a "strongly typed language",
            but it seems different from what's meant in C++. Are they
            different?
          + 9.7: I'm told Ada does all sorts of static type checking, but
            can't you get the same effect using a tool like "lint" with
            C?
          + 9.8: Does Ada have something like the Standard Template
            Library (STL) in C++, or like the components one finds in
            Smalltalk environments?
          + 9.9: Where can I find the equivalent of "printf" in Ada?


     * 10: Interfacing with Ada
          + 10.1: I am writing software that used the Distributed
            Interactive Simulation (DIS) interface, does an interface
            exist in Ada?
          + 10.2: Is there any support for Common Object Request Broker
            Architecture (CORBA) for Ada 9X?


     * 11: Finding Additional Information
          + 11.1: Where can I find Ada books?
          + 11.2: Are there other Ada-related FAQs?
          + 11.3: What is the "HBAP WWW Server"?


     * 12: Pretty-printing and Measuring Ada Source Code
          + 12.1: Is there software that generates a pretty PostScript
            file from Ada source code?
          + 12.2: I use vgrind to do "pretty printing" of my source. Is
            there a vgrind definition for Ada?
          + 12.3: How about a source code reformatter?
          + 12.4: How can I count source lines of code (SLOC)?
          + 12.5: Can I measure other things?


     * 13: Common Confusions 
          + 13.1: Wasn't Ada designed by some committee? What kind of a
            language could you possibly get from a committee?
          + 13.2: I've heard the DoD is dropping all Military standards
            to reduce costs, doesn't that mean the mandate to use Ada
            goes away too?


     * 14: Credits

     * 15: Copying this FAQ


     _________________________________________________________________

1: Recent changes to this FAQ

     * 950413: variant records vs. tagged types, submitted by Keith
       Shillington.
     * 950406: why Ada does not and should not have macros.
     * 950320: more on DIS.
     * 950315: Ada is good at numerics, by Jonathan Parker.
     * 950309: drinking and driving example.
     * 950306: added counting SLOC and measuring.
     * 950222: fixed some typos.
     * 950207: revised introduction.
     * 950202: updated discussion of C++ STL and Smalltalk library.
     * 950126: advantages of code sharing for generics; Pascal to Ada
       tool.
     * 950125: why define new integer types, and why use the predefined
       Integer type, submitted by Jonathan Parker.
     * 950124: approved for posting in *.answers.
     * 950116: converting Ada 83 code to Ada 9X.
     * 950106: lengthy code sections extracted and put on FTP server.
     * 950105: printf solution; update on exception traces and vgrind.
     * 950104: links from TOC to all questions.
     * 941222: pretty-printing solutions.
     * 941220: numbered all questions; various updates.
     * 941215: one-element aggregates.
     * 941212: additional elementary questions, submitted by Paul Pukite.
     * 941212: examining the so-called "elegance" of Eiffel (vs. Ada), by
       Tucker Taft.
     * 941212: integrated "Ada Numerics" section contributed by Jonathan
       Parker.
     * 941208: added default discriminant and "Beaujolais effect."
     * December 1994: more re-organization, HTML'ized.
     * October 1994: FAQ format by Magnus Kempe.
     * September 1994: first draft by Dave Weller.


   What's important and missing:
     * everything, life, and 42


     _________________________________________________________________


2: Information about this document

   This file is posted monthly to comp.lang.ada, comp.answers, and
   news.answers.

   This document has a home on the Home of the Brave Ada Programmers
   (HBAP) WWW Server, in hypertext format, URL
   http://lglwww.epfl.ch/Ada/FAQ/programming.html

   It is available --as posted in *.answers-- on rtfm.mit.edu, which
   archives all FAQ files posted to *.answers; see
   ftp://rtfm.mit.edu/pub/usenet-by-group/news.answers/computer-lang/Ada

   The text-only version is also available in directory
   ftp://lglftp.epfl.ch/pub/Ada/FAQ

   Magnus Kempe maintains this document; it's a hobby, not a job.
   Feedback (corrections, suggestions, ideas) about it is to be sent via
   e-mail to Magnus.Kempe@di.epfl.ch
   Thanks.

   In all cases, the most up-to-date version of the FAQ is the version
   maintained on the HBAP WWW Server. Please excuse any formatting
   inconsistencies in the posted version of this document, as it is
   automatically generated from the on-line version.

     _________________________________________________________________


3: Elementary Questions


3.1: How do I make operations directly visible without "use"ing the package?

   In Ada 83, you can rename the operations in your scope.

     -- Say you have an integer type called Int in package Types
     function "<" (Left, Right : Types.Int)
       return Boolean
       renames Types."<";
     -- Make sure the profiles of the first and last "<" match!


   For operators, Ada 95 introduces the "use type" clause:

     use type Types.Int; -- makes operators directly visible


3.2: How do I assign to an array of length 1?

   Because of ambiguity of parentheses, named notation must be used for
   one-element aggregates (or, under a different angle: a positional
   aggregate must have more than one component).

   See [RM9X 4.3.3(7)] as well as the syntax rule of
   positional_array_aggregate in [RM9X 4.3.3]; historians see [RM83
   4.3(4)].

     declare
       Array_of_One : array (1..1) of Float;
     begin
       -- Array_of_One := (10.0);   -- Won't work, parsed as an expression
                                    -- within parentheses

       Array_of_One := (1 => 10.0); -- No ambiguity here
     end;


   You can't write a one-element positional aggregate in Ada. Nor a
   zero-element aggregate. The reason for this restriction is that it
   would be difficult for compilers to determine whether:

     ( exp )

   is a parenthesized expression of some type, or an aggregate of an
   array type. If Ada had used some other notation for aggregates (say,
   "[...]"), then this problem would not exist.

   Apparently the original requirements for Ada forbade using certain
   ASCII characters, like '[' and ']', because those characters were not
   available on all hardware. Also, certain characters are used for
   different purposes and glyphs in countries that need additional
   letters not present in ASCII.


3.3: How do I create a C-style nul-terminated string?

   In a declaration block, append an ASCII.NUL to create a constant Ada
   string.

     declare
       Str_Nul : constant String := Str & ASCII.NUL;
     begin
       Call_Requiring_C_String (Str_Nul (Str_Nul'First)'Address);
     end;

-- or --

     function Nul_Terminate (Str : String)
       return String is
       Str_Nul : constant String := Str & ASCII.NUL;
     begin
       return Str_Nul;
     end Nul_Terminate;


3.4: How can I create an array of strings of various length?

   In Ada 83, you have to use string access types and "new" to get
   "ragged" arrays:

     type String_Access is
       access String;

     Strings : constant array (Positive range 1..3) of String_Access
             := ( 1 => new String'("One"),
                  2 => new String'("Two"),
                  3 => new String'("Three")
                );


   In Ada 95, the process is simplified by using aliased constants:

     type String_Access is
       access constant String;

     One : aliased constant String := "One";
     Two : aliased constant String := "Two";
     Three : aliased constant String := "Three";

     Strings : constant array (Positive range <>) of String_Access
             := ( 1 => One'Access,
                  2 => Two'Access,
                  3 => Three'Access
                );


3.5: I know an exception is raised, but my program quits with no warning. Why?


   On some Ada compilers, you have to manually "with" Text_IO before
   exception information is diplayed to the terminal.

   On other Ada compilers, you must set an environment variable flag in
   order to cause the exception information trace to be displayed.


3.6: I have only one task in my program, but it doesn't seem to run. Why?

   In Ada, the main procedure is automatically designated as a task.
   This task may be running forever, thus starving your other task(s),
   because pre-emptive scheduling is not required.

   If your Ada run-time exhibits that --pretty rare-- behavior, one
   solution is to explicitly put the main task to sleep within a loop
   construct, as in:

     procedure Main is
       task Test;
       task body Test is
       begin
         loop
           delay 1.0;
           Text_IO.Put_Line ("Test");
         end loop;
       end Test;
     begin
       loop
         delay 20.0;
         Text_IO.Put_Line ("Sleeping then writing");
       end loop;
     end Main;


3.7: How do I increase the stack size for a task?

   Define the task as a "task type" and then use a pragma representation
   clause.

     task type A_Task_Type;
     for A_Task_Type'STORAGE_SIZE use 10_000;
     -- 10K bytes allocated to instances of A_Task_Type
     A_Task : A_Task_Type;


3.8: What's the difference between a type conversion and a qualifier?

   Use a qualifier (tick) to tell the compiler what type you want; this
   incurs no run-time penalty. In other words, a qualifier "hints" the
   type.

     A : Integer := Integer'(1);  -- same as := 1
     B : Integer := Integer (1);  -- this is a conversion


3.9: How do I avoid the potential space in front of Integer'Image?

   Code a function that accepts a string and strips the leading blank.

     function Strip_Leading_Blank (Str : String)
       return String is
     begin -- Strip_Leading_Blank
       if Str (Str'First) = ' ' then
         return Str (1+Str'First .. Str'Last);
       else
         return Str;
       end if;
     end Strip_Leading_Blank;

     ...

     function My_Image (I : Integer)
       return String is
     begin -- My_Image
       return Strip_Leading_Blank (Integer'Image (I));
     end My_Image;

     ... My_Image (12) = "12" ...


3.10: Why is an exception raised when giving a default discriminant?

   Let's assume you would like to model varying-length strings:

     type V_String (Size : Natural := 0) is
       record
         S : String (1 .. Size);
       end record;


   (from Robert Dewar)

   When you give a default discriminant, then one method (I actually
   think it is the preferred method) of implementation is to allocate the
   maximum possible length. Since your discriminant is of type Natural,
   this clearly won't work!

   GNAT may compile it, but it won't run it, and indeed I consider it a
   GNAT bug (on the todo list) that no warning is issued at compile time
   for this misuse.

   Some compilers, notably Alsys and RR, have at least partially "solved"
   this problem by introducing hidden pointers, but this to me is an
   undesirable implementation choice.

   First, it means there is hidden heap activity, which seems
   undesirable. In a language where pointers are explicit, it is
   generally a good idea if allocation is also explicit, and certainly
   for real-time work, hidden anything is worrisome.

   Second, it is not easy to do uniformly. Alsys ends up introducing
   arbitrary restrictions on the composition of such types (try making an
   array of them), and RR introduces non-contiguous representations,
   which are legal but troublesome.

   To "solve" the problem yourself, just declare a reasonable maximum
   length, and use a subtype representing this length as the subtype of
   the discriminant:

     Max_Length : constant := 200;

     subtype Index is
       Natural range 0 .. Max_Length;

     type V_String (Size : Index := 0) is
       record
         S : String (1 .. Size);
       end record;


3.11: When I want an Integer type, what's wrong with just using the predefined
type Integer or Long_Integer? Why would I ever want to declare new Integer
types?

   If you declare 2 distinct integer types, for example,

     type Data_Index        is range 1..100;
     type Time_Series_Index is range 0..2**15-1;


   then objects of type Data_Index can't be assigned (directly) to
   variables of type Time_Series_Index, and vice-versa. Likewise,
   variables of these 2 types can't be mixed in arithmetical expressions
   (without explicit type conversions). This may seem like a source of
   endless irritation, but on the contrary, good progammers use it to
   improve the clarity of their code, to make it more robust, and more
   portable. The first 2 examples discuss this. The third example
   discusses the declaration of machine-portable 32-bit integers.
   Declaring objects of type Integer can be highly non-portable, and of
   course type Long_Integer may not exist on some compilers.

    Example 1.

   Suppose you declare arrays using the above indices:

     type Time_Series is array (Time_Series_Index) of Float;
     type Y_Axis_Data is array (Data_Index)        of Float;

     Measurement : Time_Series;


   Now if you mistakenly try to iterate over one array with the index of
   the other, the compiler can catch the error at compile time:

     for I in Data_Index loop
        Sum := Sum + Measurement(I);  -- compilation error
     end loop;


    Example 2.

   This is lifted from Tucker Taft's brief introduction to Ada 95 in the
   contributed papers section of the Ada World Wide Web homepage. Here
   Tucker uses the Ada 95 unsigned integers, called modular types, in the
   implementation of a protected type, which defines a disk control unit.
   Modular types are integer types with "and", "or" and "xor" defined, so
   systems programmers are likely to use them as bit masks. Just as the
   array indices of the 2 arrays defined above are never meant to be
   mixed, the modular integer types used to implement the disk control
   unit are never meant to be mixed. To make sure the compiler enforces
   this, they are declared as distinct types:

     type Flags   is mod 2**4;  -- a 4-bit flags field
     type Control is mod 2**4;  -- A 4-bit control field

     Status_Mask  : constant Flags := 2#1001#;   -- Set first and last bits.
     Status_Ready : constant Flags := 2#1000#;   -- Status = Ready

     Start_Xfr    : constant Control := 2#0001#; -- Initiate xfr command


   Now if someone attempts to apply a Flag variable where a Control
   variable should be used (or vice-versa) the compiler will catch the
   error. This is especially important when the code is maintained by
   programmers who did not write it.

    Remarks on Examples 1 and 2.

   1. Notice that in both examples the programmer was able to state his
   intentions rather forcefully in the code - intentions that otherwise
   might have been expressed much less forcefully in comment statements.
   Because of Ada's strong typing model, the compiler was able to catch
   errors at compile-time when the programmer's intentions were violated.


   2. Notice also that the Integer declarations in the 2 examples are
   machine portable, unlike Integer and Long_Integer. A compiler will
   typically map these integer types onto the most efficient base type
   that is available on the target machine.

    Example 3.

   Although the examples given above are good ones, it is not necessarily
   a common practice to define a large number of distinct integer types.
   In many cases it is appropriate to use (say) a 32-bit integer (or a
   small number of such types) and declare appropriate subtypes of it
   (them). To declare a portable 32-bit integer (or more accurately, the
   most efficient integer that is at least 32-bits):

   type Int_tmp is range -2**31+1 .. 2**31-1;
   type Integer_32 is range Int_tmp'Base'First..Int_tmp'Base'Last;


   A compiler may reject this declaration if no suitable base type is
   available, but this is rare. What happens is this: in order to
   implement Int_tmp, the compiler chooses as the base type of Int_tmp an
   integer type that is available on the target machine. This base type
   is usually the most efficient integer that accomodates the range of
   Int_tmp, which in turn is usually the machine's 32-bit integer. (It
   might even be a 64-bit integer on some machines, in which case
   Integer_32'Size = 64, and Integer_32'Last = 2**63-1. Maybe we should
   not call it Integer_32!)


3.12: Since I can always declare my own portable integer types, why would I
ever want to use the predefined type Integer?

   The language itself provides some guidance here. The predefined type
   Integer is used by Ada in the implementation of a number of convenient
   services. The following examples describe some of these services.
   Notice that in most of the following examples, it is unlikely that it
   will ever matter whether or not the predefined type Integer is
   16-bits, 32-bits, 48-bits, or 64-bits.

   a) The exponentiation of X (written X**N) is defined by the language
   for any floating point or integer X, provided N is of type Integer. (N
   should be non-negative for integer X though.)

   b) Ada's predefined String type (really just a packed unconstrained
   array of characters) uses an index of subtype Positive (i.e. type
   Integer).

   c) The array index in the following "short-hand" array declaration is
   implicitly defined to be type Integer:

     A : array(10..40) of Float;


   d) The loop parameter I in the following for loop is implicitly
   declared type Integer:

     for I in 10..40 loop
      ...
     end loop;


   This application of type Integer is the one most likely to get you
   into portability trouble. If you write: "for I in 1..2**17 loop", then
   you get a constraint error on compilers that make Integer 16-bits,
   because 2**17 is out of range of any Ada 16-bit integer.


3.13: I am learning Ada. Can I experiment with a game program?

   Of course. The Public Ada library (FTP wuarchive.wustl.edu) has a
   portable Ada Tetris program in the languages/ada/misc/games directory.
   It uses tasking, keyboard input, and ANSI screen graphics. Have fun!

     _________________________________________________________________


4: Advantages of Ada


4.1: Why use Ada?

   Think of it like this: We're the kid on the street corner, licking
   that tasty ice cream cone on a hot summer day; an impish grin
   decorates our face as we consume our cool confection. Meanwhile, other
   kids gather round, noticing our pleasure. It matters not a whit that
   they've just had a drink, or had their fill with supper -- they now
   want ice cream. We offer no lecture on how good the ice cream is, we
   simply demonstrate that we are happy, and let their memories carry
   them to the nearest ice cream truck.

   (Sorry, I got a little carried away --DW).


4.2: Ada seems large and complex, why is it this way?

   (Robert Dewar, lead designer of the GNU Ada compiler, responds):

   During the Ada 9X development process we have often had fierce
   arguments over the need to simplify proposals, and I pointed out some
   time ago that the idea of simplicity is heavily overloaded:
     * simple to implement
     * simple to describe informally
     * simple to describe formally
     * results in simple programs
     * simple to understand and/or remember
     * short to describe


   None of these goals are quite the same, and often they severely
   conflict.

   If you listen to programming language design types, especially from
   universities, they often have very little experience in programming,
   and especially little experience in writing large delivered,
   maintained software. That doesn't mean they know nothing about
   programming languages, but it does tend to mean that their view of
   complexity is skewed, and in particularly concentrates on the
   simplicity of the language itself, rather than on the simplicity of
   resulting programs.

   A lot of the creative tension in the 9X design process arose from this
   same fundamental dichotomy. The design team tended to have a high
   tolerance for language complexity (partly because they were very good
   at understanding language details), but had a lot of experience in
   actual large scale programming, and so their idea of simplicity was
   biased heavily to simplifying Ada programs. The opposite voice,
   worried about the simplicity of the language itself, represented by a
   section of the DR's and ISO group (who, being a larger more diverse
   group tended to reflect a wider view), considered that the design team
   had gone too far in this direction. If you want to get a feel for the
   transitions, look at the early versions of the 9X ILS, particularly
   version 1.0.

   In retrospect, I think we came up with what is at least very close the
   optimal balance. Tuck can speak for himself here more clearly than I
   can speak for him, but I would guess that he and the other members of
   the team recognize that you have to be able to sell the resulting
   design as an acceptably simple whole, and thus must step back from the
   most extensive proposals, while on the other hand, the more
   conservative KISS sentiments were convinced to accept more features
   than they originally felt comfortable with because of convincing
   programming examples and discussions of resulting programming
   complexity. The third wing of opinion ("I don't care what you think,
   but if we can't implement it, then it's not much use!") was also
   effectively fed in from the user-implementor teams.

   Is the result too complex? Time will tell, but I think the balance is
   a successful blend.

   I am a little more worried about C++ since there is an even greater
   danger of kitchen sink mentality, fueled by lots of enthusiastic
   feature-hungry programmers, adding too much to C++. A lot of big new
   features have been added to C++ recently (notably templates and
   exceptions). It's not that these new features are useless (after all
   Ada programmers know the value of generics and exceptions!) but there
   is a tendency to always add things in a most-decorated form (the
   templates in C++ are much more general, but less safe, than Ada
   generics, and the exceptions in C++ are much more elaborate, but less
   efficient to implement, and trickier to understand). I think it will
   be important for the C++ guys to make a real effort to keep the
   continued development of the language under control to avoid going too
   far in the feature-rich direction.

     _________________________________________________________________


5: Object-Oriented Programming with Ada


5.1: Why does Ada have "tagged types" instead of classes?

   (Tucker Taft responds):

   Someone recently asked me to explain the difference between the
   meaning of the term "class" in C++ and its meaning in Ada 9X. Here is
   a synopsis of the answer:

   In C++, the term "class" refers to three different, but related
   things:
     * a language construct, that encapsulates the definitions of data
       members, member functions, nested types, etc.;

     * a particular kind of type, defined by a class construct (or by
       "struct" which is a special case of "class");

     * a set of types consisting of a type and all of its derivatives,
       direct and indirect.


   In Ada 9X, the term "class" refers only to the third of the above
   definitions. Ada 9X (and Ada 83) has three different terms for the
   concepts corresponding to the above three things:
     * a "package" encapsulates the definitions of types, objects,
       operations, exceptions, etc which are logically related. (The
       operations of a type defined immediately within the package where
       the type is declared are called, in 9X, the "primitive operations"
       of the type, and in some sense, define the "primitive" semantics
       of the type, especially if it is a private type.)

     * a "type" is characterized by a set of values and a set of
       primitive operations (there are a million definitions of "type,"
       unfortunately, but you know what I mean...);

     * a "class" is a set of types with similar values and operations; in
       particular, a type and and all of its derivatives, direct and
       indirect, represents a (derivation) class. Also, the set of
       integer types form the integer "class," and so on for the other
       language-defined classes of types in the language.


   Some OOP languages take an intermediary position. In CLOS, a "class"
   is not an encapsulating construct (CLOS has "packages"). However, a
   "class" is both a type and a set of types, depending on context.
   (Methods "float" freely.)

   The distinction Ada 9X makes between types and classes (= set of
   types) carries over into the semantic model, and allows some
   interesting capabilities not present in C++. In particular, in Ada 9X
   one can declare a "class-wide" object initialized by copy from a
   "class-wide" formal parameter, with the new object carrying over the
   underlying type of the actual parameter. For example:

     procedure Print_In_Bold (X : T'Class) is
       -- Copy X, make it bold face, and then print it.
       Copy_Of_X : T'Class := X;
     begin
        Make_Bold (Copy_Of_X);
        Print (Copy_Of_X);
     end P;


   In C++, when you declare an object, you must specify the "exact" class
   of the object -- it cannot be determined by the underlying class of
   the initializing value. Implementing the above procedure in a general
   way in C++ would be slightly more tedious.

   Similarly, in Ada 9X one can define an access type that designates
   only one specific type, or alternatively, one can define one that can
   designate objects of any type in a class (a "class-wide" access type).
   For example:

     type Fancy_Window_Ptr is access Fancy_Window;
       -- Only points at Fancy Windows -- no derivatives allowed
     type Any_Window_Ptr is access Window'Class;
       -- Points at Windows, and any derivatives thereof.


   In C++, all pointers/references are "class-wide" in this sense; you
   can't restrict them to point at only one "specific" type.

   In other words, C++ makes the distinction between "specific" and
   "class-wide" based on pointer/reference versus object/value, whereas
   in Ada 9X, this distinction is explicit, and corresponds to the
   distinction between "type" (one specific type) and "class" (set of
   types).

   The Ada 9X approach we believe (hope ;-) gives somewhat better control
   over static versus dynamic binding, and is less error prone since it
   is type-based, rather than being based on reference vs. value.

   In any case, in Ada 9X, C++, and CLOS it makes sense to talk about
   "class libraries," since a given library will generally consist of a
   set of interrelated types. In Ada 9X and CLOS, one could alternatively
   talk about a set of "reusable packages" and mean essentially the same
   thing.


5.2: Variant records seem like a dead feature now. When should I use them
instead of tagged types?

   This is an instance of a much more general question: "When should I
   use what kind of type?" The simple answer is: "When it makes sense to
   do so." The real key to chosing a type in Ada is to look at the
   application, and pick the type that most closely models the problem.

   For instance, if you are modelling data transmission where the message
   packets may contain variable forms of data, a variant record --not a
   hierarchy of tagged types-- is an appropriate model, since there may
   be no relationship between the data items other than their being
   transmitted over one channel. If you choose to model the base type of
   the messages with a tagged type, that may present more problems than
   it solves when communicating across distinct architectures.

   [More to be said about variant programming vs. incremental
   programming.]


5.3: What is meant by "interface inheritance" and how does Ada support it?

   This answer intentionally left blank.


5.4: How do you do multiple inheritance in Ada 9X?

   There is a lengthy paper in file
   ftp://sw-eng.falls-church.va.us/public/AdaIC/docs/9Xproject/9xm-inh.txt

   That document describes several mechanisms for achieving MI in Ada. It
   is not unusual, however, to find complaints about the syntax and the
   perceived burden it places on the developer. This is what Tucker Taft
   had to say when responging to such a criticism on comp.lang.ada:

   Coming up with a syntax for multiple inheritance was not the
   challenge. The challenge was coming up with a set of straightforward
   yet flexible rules for resolving the well known problems associated
   with multiple inheritance, namely:
     * If the same type appears as an ancestor more than once, should all
       or some of its data components be duplicated, or shared? If any
       are duplicated, how are they referenced unambiguously?

     * If the same-named (including same parameter/result profile)
       operation is inherited along two paths, how is the ambiguity
       resolved? Can you override each with different code? How do you
       refer to them later?

     * Etc...


   For answers, you can look at the various languages that define a
   built-in approach to multiple inheritance. Unfortunately, you will
   generally get a different answer for each language -- hardly a
   situation that suggests we will be able to craft an international
   consensus. Eiffel uses renaming and other techniques, which seem quite
   flexible, but at least in some examples, can be quite confusing (where
   you override "B" to change what "A" does in some distant ancestor).
   C++ has both non-virtual and virtual base clases, with a number of
   rules associated with each, and various limitations relating to
   downcasting and virtual base classes. CLOS uses simple name matching
   to control "slot" merging. Some languages require that all but one of
   the parent types be abstract, data-less types, so only interfaces are
   being inherited; however if the interfaces happen to collide, you
   still can end up with undesirable and potentially unresolvable
   collisions (where you really want different code for same-named
   interfaces inherited from different ancestors).

   One argument is that collisions are rare to begin with, so it doesn't
   make much different how they are resolved. That is probably true, but
   the argument doesn't work too well during an open language design
   process -- people get upset at the most unbelievably trivial and
   rarely uses features if not "correctly" designed (speaking from
   experience here ;-).

   Furthermore, given that many of the predominant uses of MI (separation
   of interface inheritance from implementation inheritance, gaining
   convenient access to another class's features, has-a relationships
   being coded using MI for convenience, etc.) are already handled very
   well in Ada 9X, it is hard to justify getting into the MI language
   design fray at all. As you nicely point out, the basic inheritance
   model in Ada 9X is simple and elegant. Why clutter it up with a lot of
   relatively ad-hoc rules to handle one particular approach to MI? For
   the rare cases where MI is really critical, the last thing the
   programmer wants in the language is the "wrong" MI approach built in.

   So the basic answer is that at this point in the evolution of OO
   language design, it seemed wiser to provide MI building blocks, rather
   than to foist the wrong approach on the programmer, and be regretting
   it and working around it for years to come.

   Perhaps [Douglas Arndt] said it best...

     Final note: inheritance is overrated, especially MI. ...


   If the only or primary type composition mechanism in the language is
   based on inheritance, then by all means, load it up. But Ada 9X
   provides several efficient and flexible type composition mechanisms,
   and there is no need to overburden inheritance with unnecessary and
   complicated baggage.


5.5: Why are Controlled types so, well, strange?

   (Tucker Taft responds):

   We considered many approaches to user-defined finalization and
   user-defined assignment. Ada presents challenges that make it harder
   to define assignment than in other languages, because assignment is
   used implicitly in several operations (by-copy parameter passing,
   function return, aggregates, object initialization, initialized
   allocators, etc.), and because Ada has types whose set of components
   can be changed as a result of an assignment.

   For example:

     type T (D : Boolean := False) is record
       case D is
         when False => null;
         when True => H : In_Hands;
       end case;
     end record;

     X,Z : T;
     Y : T := (True, H => ...);

     ...

     X := Y;   -- "X.H" component coming into existence
     Y := Z;   -- "Y.H" component going out of existence


   With a type like the one above, there are components that can come and
   go as a result of assignment. The most obvious definition of
   assignment would be:

     procedure ":="(Left : in out In_Hands; Right : in In_Hands);


   Unfortunately, this wouldn't work for the "H" component, because there
   is no preexisting "In_Hands" component to be assigned into in the
   first case, and in the second case, there is no "In_Hands" component
   to assign "from."

   Therefore, we decided to decompose the operation of assignment into
   separable pieces: finalization of the left hand side; simple copying
   of the data from the right hand side to the left hand side; and then
   adjustment of the new left hand side. Other decompositions are
   probably possible, but they generally suffer from not being easily
   composable, or not handling situations like the variant record above.

   You seem to be proposing a function named ":=" that presumably returns
   a copy of its in parameter. However, to do anything interesting it
   will have to copy the in parameter into a local variable, and then
   "fiddle" with than local variable (essentially what "Adjust" does),
   and then return that local variable (which will make yet another
   copy). The returned result will have to be put back into the desired
   place (which might make yet another copy). For a large object, this
   might involve several extra copies.

   By having the user write just that part of the operation that
   "fiddles" with the result after making a copy, we allow the
   implementation to eliminate redundant copying. Furthermore, some
   user-defined representations might be position dependent. That is, the
   final "fiddling" has to take place on the object in its final
   location. For example, one might want the object to point to itself.
   If the implementation copies an object after the user code has
   adjusted it, such self-references will no longer point to the right
   place.

   So, as usual, once one gets into working out the details and all the
   interactions, the "obvious" proposal (such as a procedure ":=") no
   longer looks like the best answer, and the best answer one can find
   potentially looks "clumsy" (at least before you try to work out the
   details of the alternatives).




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

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

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
1995-03-21 18:10 Ada FAQ: Programming with Ada (part 1 of 3) Magnus Kempe
  -- strict thread matches above, loose matches on Subject: below --
1995-04-20  0:00 Magnus Kempe
1995-01-19 18:04 Magnus Kempe

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