From: Magnus.Kempe@di.epfl.ch (Magnus Kempe)
Subject: Ada FAQ: Programming with Ada (part 2 of 3)
Date: 19 Jan 1995 18:10:28 GMT
Date: 1995-01-19T18:10:28+00:00 [thread overview]
Message-ID: <3fm9uk$a72@disunms.epfl.ch> (raw)
Archive-name: computer-lang/Ada/programming/part2
Comp-lang-ada-archive-name: programming/part2
Posting-Frequency: monthly
Last-modified: 19 January 1995
Last-posted: the epoch
Ada Programmer'S
Frequently Asked Questions (FAQ)
This is part 2 of a 3-part posting.
Part 3 begins with question 9.6; it should be the next posting in this thread.
Part 1 should be the previous posting in this thread.
5.7: What is meant by upcasting/expanding and downcasting/narrowing?
(Tucker Taft replies):
Here is the symmetric case to illustrate upcasting and downcasting.
type A is tagged ...; -- one parent type
type B is tagged ...; -- another parent type
...
type C; -- the new type, to be a mixture of A and B
type AC (Obj : access C'Class) is
new A
with ...;
-- an extension of A to be mixed into C
type BC (Obj : access C'Class) is
new B
with ...;
-- an extension of B to be mixed into C
type C is
tagged limited record
A : AC (C'Access);
B : BC (C'Access);
... -- other stuff if desired
end record;
We can now pass an object of type C to anything that takes an A or B
as follows (this presumes that Foobar and QBert are primitives of A
and B, respectively, so they are inherited; if not, then an explicit
conversion (upcast) to A and B could be used to call the original
Foobar and QBert).
XC : C;
...
Foobar (XC.A);
QBert (XC.B);
If we want to override what Foobar does, then we override Foobar on
AC. If we want to override what QBert does, then we override QBert on
BC.
Note that there are no naming conflicts, since AC and BC are distinct
types, so even if A and B have same-named components or operations, we
can talk about them and/or override them individually using AC and BC.
Upcasting (from C to A or C to B) is trivial -- A(XC.A) upcasts to A;
B(XC.B) upcasts to B.
Downcasting (narrowing) is also straightforward and safe. Presuming XA
of type A'Class, and XB of type B'Class:
AC(XA).Obj.all downcasts to C'Class (and verifies XA in AC'Class)
BC(XB).Obj.all downcasts to C'Class (and verifies XB in BC'Class)
You can check before the downcast to avoid a Constraint_Error:
if XA not in AC'Class then -- appropriate complaint
if XB not in BC'Class then -- ditto
The approach is slightly simpler (though less symmetric) if we choose
to make A the "primary" parent and B a "secondary" parent:
type A is ...
type B is ...
type C;
type BC (Obj : access C'Class) is
new B
with ...
type C is
new A
with record
B : BC (C'Access);
... -- other stuff if desired
end record;
Now C is a "normal" extension of A, and upcasting from C to A and
(checked) downcasting from C'Class to A (or A'Class) is done with
simple type conversions. The relationship between C and B is as above
in the symmetric approach.
Not surprisingly, using building blocks is more work than using a
"builtin" approach for simple cases that happen to match the builtin
approach, but having building blocks does ultimately provide mean more
flexibility for the programmer -- there are many other structures that
are possible in addition to the two illustrated above, using the
access discriminant building block.
For example, for mixins, each mixin "flavor" would have an access
discriminant already:
type Window is ... -- The basic "vanilla" window
-- Various mixins
type Win_Mixin_1 (W : access Window'Class) is ...
type Win_Mixin_2 (W : access Window'Class) is ...
type Win_Mixin_3 (W : access Window'Class) is ...
Given the above vanilla window, plus any number of window mixins, one
can construct a desired window by including as many mixins as wanted:
type My_Window is Window with
M1 : Win_Mixin_1 (My_Window'access);
M3 : Win_Mixin_3 (My_Window'access);
M11 : Win_Mixin_1(My_Window'access);
... -- plus additional stuff, as desired.
end record;
As illustrated above, you can incorporate the same "mixin" multiple
times, with no naming conflicts. Every mixin can get access to the
enclosing object. Operations of individual mixins can be overridden by
creating an extension of the mixin first, overriding the operation in
that, and then incorporating that tweaked mixin into the ultimate
window.
I hope the above helps better illustrate the use and flexibility of
the Ada 9X type composition building blocks.
5.8: How does Ada do "narrowing"?
Dave Griffith said
. . . Nonetheless, The Ada9x committee chose a structure-based
subtyping, with all of the problems that that is known to cause. As
the problems of structure based subtyping usually manifest only in
large projects maintained by large groups, this is _precisely_ the
subtype paradigm that Ada9x should have avoided. Ada9x's model is,
as Tucker Taft pointed out, quite easy to use for simple OO
programming. There is, however, no good reason to _do_ simple OO
programming. OO programmings gains click in somewhere around 10,000
LOC, with greatest gains at over 100,000. At these sizes, "just
declare it tagged" will result in unmaintainable messes. OO
programming in the large rapidly gets difficult with structure based
subtyping. Allowing by-value semantics for objects compounds these
problems. All of this is known. All of this was, seemingly, ignored
by Ada9x.
(Tucker Taft answers)
As explained in a previous note, Ada 9X supports the ability to hide
the implementation heritage of a type, and only expose the desired
interface heritage. So we are not stuck with strictly "structure-based
subtyping." Secondly, by-reference semantics have many "well known"
problems as well, and the designers of Modula-3 chose to, seemingly,
ignore those ;-) ;-). Of course, in reality, neither set of language
designers ignored either of these issues. Language design involves
tradeoffs. You can complain we made the wrong tradeoff, but to
continue to harp on the claim that we "ignored" things is silly. We
studied every OOP language under the sun on which we could find any
written or electronic material. We chose value-based semantics for
what we believe are good reasons, based on reasonable tradeoffs.
First of all, in the absence of an integrated garbage collector,
by-reference semantics doesn't make much sense. Based on various
tradeoffs, we decided against requiring an integrated garbage
collector for Ada 9X.
Secondly, many of the "known" problems with by-value semantics we
avoided, by eliminating essentially all cases of "implicit
truncation." One of the problems with the C++ version of "value
semantics" is that on assignment and parameter passing, implicit
truncation can take place mysteriously, meaning that a value that
started its life representing one kind of thing gets truncated
unintentionally so that it looks like a value of some ancestor type.
This is largely because the name of a C++ class means differnt things
depending on the context. When you declare an object, the name of the
class determines the "exact class" of the object. The same thing
applies to a by-value parameter. However, for references and pointers,
the name of a class stands for that class and all of its derivatives.
But since, in C++, a value of a subclass is always acceptable where a
value of a given class is expected, you can get implicit truncation as
part of assignment and by-value parameter passing. In Ada 9X, we avoid
the implicit truncation because we support assignment for "class-wide"
types, which never implicitly truncates, and one must do an explicit
conversion to do an assignment that truncates. Parameter passing never
implicitly truncates, even if an implicit conversion is performed as
part of calling an inherited subprogram.
In any case, why not either ignore Ada 9X or give it a fair shot? It
is easy to criticize any particular design decision, but it is much
harder to actually put together a complete integrated language design
that meets the requirements of its user community, doesn't bankrupt
the vendor community, and provides interesting fodder for the academic
community ;-).
_________________________________________________________________
6: Ada Numerics
6.1: Where can I find anonymous ftp sites for Ada math packages? In particular
where are the random number generators?
bugs.nosc.mil (128.49.4.117)
Stuff of high quality in pub/ada The random number generator
and random deviates are recommended. These are mirrored at the
next site, wuarchive.
ftp.rational.com
Freeware version of the ISO math packages on Rational's FTP
server. It's a binding over the C Math library, in
public/apex/freeware/math_lib.tar.Z
wuarchive.wustl.edu
Site of PAL, the Public Ada Library: math routines scattered
about in the directories under languages/ada in particular, in
subdirectory swcomps
source.asset.com
This is not an anonymous ftp site for math software. What you
should do is log on anonymously under ftp, and download the
file asset.faq from the directory pub. This will tell you how
to get an account.
ftp.cs.kuleuven.ac.be
Go to directory pub/Ada-Belgium/cdrom. There's a collection of
math intensive software in directory swcomps. Mirrors some of
PAL at wuarchive.wustl.edu.
ajpo.sei.cmu.edu
Go to directory public/atip/adar to find extended-precision
decimal arithmetic. Includes facilities for COBOL-like IO.
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?
(from Jonathan Parker)
Ada 83 was slow to arrive at a standard naming convention for
elementary math functions and complex numbers. Furthermore, you'll
find that some compilers call the 64-bit floating point type
Long_Float; other compilers call it Float. Fortunately, it is easy to
write programs in Ada that are independent of the naming conventions
for floating point types and independent of the naming conventions of
math functions defined on those types.
One of the cleanest ways is to make the program generic:
generic
type Real is digits <>;
with function Arcsin (X : Real) return Real is <>;
with function Log (X : Real) return Real is <>;
-- This is the natural log, inverse of Exp(X), sometimes written Ln(X).
package Example_1 is
...
end Example_1;
So the above package doesn't care what the name of the floating point
type is, or what package the Math functions are defined in, just as
long as the floating point type has the right attributes (precision
and range) for the algorithm, and likewise the functions. Everything
in the body of Example_1 is written in terms of the abstract names,
Real, Arcsin, and Log, even though you instantiate it with compiler
specific names that can look very different:
package Special_Case is new Example_1 (Long_Float, Asin, Ln);
The numerical algorithms implemented by generics like Example_1 can
usually be made to work for a range of floating point precisions. A
well written program will perform tests on Real to reject
instantiations of Example_1 if the floating points type is judged
inadequate. The tests may check the number of digits of precision in
Real (Real'Digits) or the range of Real (Real'First, Real'Last) or the
largest exponent of the set of safe numbers (Real'Safe_Emax), etc.
These tests are often placed after the begin statement of package
body, as in:
package body Example_1 is
...
begin
if (Real'Machine_Mantissa > 60) or (Real'Machine_Emax < 256) then
raise Program_Error;
end if;
end Example_1;
Making an algorithm as abstract as possible, (independent of data
types as much as possible) can do a lot to improve the quality of the
code. Support for abstraction is one of the many things Ada-philes
find so attractive about the language. The designers of Ada 95
recognized the value of abstraction in the design of numeric
algorithms and have generalized many of the features of the '83 model.
For example, no matter what floating point type you instantiate
Example_1 with, Ada 95 provides you with functions for examining the
exponent and the mantissas of the numbers, for truncating, determining
exact remainders, scaling exponents, and so on. (In the body of
Example_1, and in its spec also of course, these functions are
written, respectively: Real'Exponent(X), Real'Fraction(X),
Real'Truncation(X), Real'Remainder(X,Y), Real'Scaling(X, N). There are
others.) Also, in package Example_1, Ada 95 lets you do the arithmetic
on the base type of Real (called Real'Base) which is liable to have
greater precision and range than type Real.
It is rare to see a performance loss when using generics like this.
However, if there is an unacceptable performance hit, or if generics
cannot be used for some other reason, then subtyping and renaming will
do the job. Here is an example of renaming:
with Someones_Math_Lib;
procedure Example_2 is
subtype Real is Long_Float;
package Math renames Someones_Math_Lib;
function Arcsin(X : Real) return Real renames Math.Asin
function Log (X : Real) return Real renames Math. Ln;
-- Everything beyond this point is abstract with respect to
-- the names of the floating point (Real), the functions (Arcsin
-- and Log), and the package that exported them (Math).
...
end Example_2;
I prefer to make every package and subprogram (even test procedures)
as compiler independent and machine portable as possible. To do this
you move all of the renaming of compiler dependent functions and all
of the "withing" of compiler dependent packages to a single package.
In the example that follows, its called Math_Lib_8. Math_Lib_8 renames
the 8-byte floating point type to Real_8, and makes sure the math
functions follow the Ada 95 standard, at least in name. In this
approach Math_Lib_8 is the only compiler dependent component.
There are other, perhaps better, ways also. See for example, "Ada In
Action", by Do-While Jones for a generic solution.
Here's the spec of Math_Lib_8, which is a perfect subset of package
Math_Env_8, available by FTP in file
ftp://lglftp.epfl.ch/pub/Ada/FAQ/math_env_8.ada
--***************************************************************
-- Package Math_Lib_8
--
-- A minimal math package for Ada 83: creates a standard interface to vendor
-- specific double-precision (8-byte) math libraries. It renames the 8 byte
-- Floating point type to Real_8, and uses renaming to create
-- (Ada 95) standard names for Sin, Cos, Log, Sqrt, Arcsin, Exp,
-- and Real_8_Floor, all defined for Real_8.
--
-- A more ambitious but perhaps less efficient
-- package would wrap the compiler specific functions in function calls, and
-- do error handling on the arguments to Ada 95 standards.
--
-- The package assumes that Real_8'Digits > 13, and that
-- Real_8'Machine_Mantissa < 61. These are asserted after the
-- begin statement in the body.
--
-- Some Ada 83 compilers don't provide Arcsin, so a rational-polynomial+
-- Newton-Raphson method Arcsin and Arccos pair are provided in the body.
--
-- Some Ada 83 compilers don't provide for truncation of 8 byte floats.
-- Truncation is provided here in software for Compilers that don't have it.
-- The Ada 95 function for truncating (toward neg infinity) is called 'Floor.
--
-- The names of the functions exported below agree with the Ada9X standard,
-- but not, in all likelihood the semantics. It is up to the user to
-- be careful...to do his own error handling on the arguments, etc.
-- The performance of these function can be non-portable,
-- but in practice they have their usual meanings unless you choose
-- weird arguments. The issues are the same with most math libraries.
--***************************************************************
--with Math_Lib; -- Meridian DOS Ada.
with Long_Float_Math_Lib; -- Dec VMS
--with Ada.Numerics.Generic_Elementary_Functions; -- Ada9X
package Math_Lib_8 is
--subtype Real_8 is Float; -- Meridian 8-byte Real
subtype Real_8 is Long_Float; -- Dec VMS 8-byte Real
--package Math renames Math_Lib; -- Meridian DOS Ada
package Math renames Long_Float_Math_Lib; -- Dec VMS
--package Math is new Ada.Numerics.Generic_Elementary_Functions(Real_8);
-- The above instantiation of the Ada.Numerics child package works on
-- GNAT, or any other Ada 95 compiler. Its here if you want to use
-- an Ada 95 compiler to compile Ada 83 programs based on this package.
function Cos (X : Real_8) return Real_8 renames Math.Cos;
function Sin (X : Real_8) return Real_8 renames Math.Sin;
function Sqrt(X : Real_8) return Real_8 renames Math.Sqrt;
function Exp (X : Real_8) return Real_8 renames Math.Exp;
--function Log (X : Real_8) return Real_8 renames Math.Ln; -- Meridian
function Log (X : Real_8) return Real_8 renames Math.Log; -- Dec VMS
--function Log (X : Real_8) return Real_8 renames Math.Log; -- Ada 95
--function Arcsin (X : Real_8) return Real_8 renames Math.Asin; -- Dec VMS
--function Arcsin (X : Real_8) return Real_8 renames Math.Arcsin; -- Ada 95
function Arcsin (X : Real_8) return Real_8;
-- Implemented in the body. Should work with any compiler.
--function Arccos (X : Real_8) return Real_8 renames Math.Acos; -- Dec VMS
--function Arccos (X : Real_8) return Real_8 renames Math.Arccos; -- Ada 95
function Arccos (X : Real_8) return Real_8;
-- Implemented in the body. Should work with any compiler.
--function Real_8_Floor (X : Real_8) return Real_8 renames Real_8'Floor;-- 95
function Real_8_Floor (X : Real_8) return Real_8;
-- Implemented in the body. Should work with any compiler.
end Math_Lib_8;
6.3: Where's a good place to start learning the Ada 95 numerics model?
(from Jonathan Parker)
Start with the Ada 95 Rationale. Part 3 of the Rationale, Section 1.3
provides a good introduction, with examples on use of elementary
function packages. Part 3 of the Rationale, Section 6.1 discusses the
design of the Complex number and Complex function packages. Section 6
reviews the numerics annex, especially attributes and accuracy
requirement.
The Rationale can be obtained by anonymous ftp from ajpo.sei.cmu.edu.
Another site to get the Ada 95 Rationale from is the Ada WWW homepage
at http://lglwww.epfl.ch/Ada/ .
The Ada 95 Reference Manual describes the Real valued elementary Math
functions, the Random number packages, and the floating point
attributes in section A5 (page 295). The Ada 95 Reference Manual may
be obtained by anonymous ftp from ajpo.sei.cmu.edu, in directory
public/ada9x/rm9x.
6.4: How do I get Real valued and Complex valued math functions in Ada 95?
(from Jonathan Parker)
Complex type and functions are provided by compilers that support the
numerics Annex. The packages that use Float for the Real number and
for the Complex number are:
Ada.Numerics.Elementary_Functions;
Ada.Numerics.Complex_Types;
Ada.Numerics.Complex_Elementary_Functions;
The packages that use Long_Float for the Real number and for the
Complex number are:
Ada.Numerics.Long_Elementary_Functions;
Ada.Numerics.Long_Complex_Types;
Ada.Numerics.Long_Complex_Elementary_Functions;
The generic versions are demonstrated in the following example. Keep
in mind that the non-generic packages may have been better tuned for
speed or accuracy. In practice you won't always instantiate all three
packages at the same time, but here is how you do it:
with Ada.Numerics.Generic_Complex_Types;
with Ada.Numerics.Generic_Elementary_Functions;
with Ada.Numerics.Generic_Complex_Elementary_Functions;
procedure Do_Something_Numerical is
type Real_8 is digits 15;
package Real_Functions_8 is
new Ada.Numerics.Generic_Elementary_Functions (Real_8);
package Complex_Nums_8 is
new Ada.Numerics.Generic_Complex_Types (Real_8);
package Complex_Functions_8 is
new Ada.Numerics.Generic_Complex_Elementary_Functions
(Complex_Nums_8);
use Real_Functions_8, Complex_Nums_8, Complex_Functions_8;
...
... -- Do something
...
end Do_Something_Numerical;
6.5: What libraries or public algorithms exist for Ada?
An Ada version of Fast Fourier Transform is available. It's in
journal "Computers & Mathematics with Applications," vol. 26, no. 2,
pp. 61-65, 1993, with the title:
"Analysis of an Ada Based Version of Glassman's General N Point Fast
Fourier Transform"
The package is now available in the AdaNET repository, object #: 6728,
in collection: Transforms. If you're not an AdaNET user, contact Peggy
Lacey (lacey@rbse.mountain.net).
_________________________________________________________________
7: Efficiency of Ada Constructs
7.1: How much extra overhead do generics have?
(Submitted by Robert Eachus)
If you overgeneralize the generic, there really will be more work to
do. How do you know when you have overgeneralized? You don't. But
passing arithmetic operations as parameters is a bad sign. So are
boolean or enumeration type generic formal parameters. If you never
override the defaults for a parameter, you almost certainly
overengineered. (The last statement is not absolute only because I
have created cases where the defaults were different for different
instantiations and never needed to be overridden. I'd call that
overelegant instead.)
Code sharing (if implemented and requested) will cause an additional
overhead on some calls, which will be partially offset by improved
locality of reference. (Translation, code sharing wins most when cache
misses cost most.) Of course, if a generic is only used once in a
program, code sharing always loses.
Generics in Ada can also result in loss of information which can help
the optimizer. Since the compiler is not restricted by Ada staticness
rules within a single module, you can often avoid penalties by
declaring (or redeclaring) bounds so that they are local:
with Lots_of_Things;
package Global is
subtype Global_Int is
Integer range X..Y;
...
end Global;
with Global;
package Local is
subtype Global_Int is
Global.Global_Int;
package Some_Instance is
new Foo(Global_Int);
...
end Local;
Ada rules say that having the subtype redeclared locally does not
affect staticness, but on a few occasions I have caught optimizers
doing a much better job. Of course, optimizers are constantly
changing, so I may have just caught one at the wrong time.
_________________________________________________________________
8: Advanced Programming Techniques with Ada
8.1: Does Ada have automatic constructors and destructors?
(Tucker Taft replies)
At least in Ada 9X, functions with controlling results are inherited
(even if overriding is required), allowing their use with dynamic
binding and class-wide types. In most other OOPs, constructors can
only be called if you know at compile time the "tag" (or equivalent)
of the result you want. In Ada 9X, you can use the tag determined by
the context to control dispatching to a function with a controlling
result. For example:
type Set is abstract tagged private;
function Empty return Set is abstract;
function Unit_Set(Element : Element_Type) return Set is abstract;
procedure Remove(S : in out Set; Element : out Element_Type) is abstract;
function Union(Left, Right : Set) return Set is abstract;
...
procedure Convert(Source : Set'Class; Target : out Set'Class) is
-- class-wide "convert" routine, can convert one representation
-- of a set into another, so long as both set types are
-- derived from "Set," either directly or indirectly.
-- Algorithm: Initialize Target to the empty set, and then
-- copy all elements from Source set to Target set.
Copy_Of_Source : Set'Class := Source;
Element : Element_Type;
begin
Target := Empty; -- Dispatching for Empty determined by Target'Tag.
while Copy_Of_Source /= Empty loop
-- Dispatching for Empty based on Copy_Of_Source'Tag
Remove_Element(Copy_Of_Source, Element);
Target := Union(Target, Unit_Set(Element));
-- Dispatching for Unit_Set based on Target'Tag
end loop;
end Convert;
The functions Unit_Set and Empty are essentially "constructors" and
hence must be overridden in every extension of the abstract type Set.
However, these operations can still be called with a class-wide
expected type, and the controlling tag for the function calls will be
determined at run-time by the context, analogous to the kind of
(compile-time) overload resolution that uses context to disambiguate
enumeration literals and aggregates.
8.2: How can I redefine assignment operations?
See "Tips and Tidbits #1: User Defined Assignment" by Brad Balfour
(where is this located?)
8.3: Should I stick to a one package, one type approach while writing Ada
software?
(Robb Nebbe responds)
Off hand I can think of a couple of advantages from separating the
concepts of type and module in Ada.
Separation of visibility and inheritance allows a programmer to
isolate a derived type from the implementation details of its parent.
To put it another way information hiding becomes a design decision
instead of a decision that the programming language has already made
for you.
Another advantage that came "for free" is the distinction between
subtyping and implementation inheritance. Since modules and types are
independent concepts the interaction of the facilities for information
hiding already present in Ada83 with inheritance provide an elegant
solution to separating subtyping from implementation inheritance. (In
my opinion more elegant than providing multiple forms of inheritance
or two distinct language constructs.)
8.4: What is the "Beaujolais Effect"?
The "Beaujolais Effect" is detrimental, and language designers should
try to avoid it. But what is it?
(from Tucker Taft)
The term "Beaujolais Effect" comes from a prize (a bottle of
Beaujolais) offered by Jean Ichbiah during the original Ada design
process to anyone who could find a situation where adding or removing
a single "use" clause could change a program from one legal
interpretation to a different legal interpretation. (Or equivalently,
adding or removing a single declaration from a "use"d package.)
At least one bottle was awarded, and if the offer was still open, a
few more might have been awarded during the Ada 9X process. However,
thanks to some very nice analysis by the Ada 9X Language Precision
Team (based at Odyssey Research Associates) we were able to identify
the remaining cases of this effect in Ada 83, and remove them as part
of the 9X process.
The existing cases in Ada 83 had to do with implicit conversion of
expressions of a universal type to a non-universal type. The rules in
Ada 9X are subtly different, making any case that used to result in a
Beaujolais effect in Ada 83, illegal (due to ambiguity) in Ada 9X.
The Beaujolais effect is considered "harmful" because it is expected
that during maintenance, declarations may be added or removed from
packages without being able to do an exhaustive search for all places
where the package is "use"d. If there were situations in the language
which resulted in Beaujolais effects, then certain kinds of changes in
"use"d packages might have mysterious effects in unexpected places.
(from Jean D. Ichbiah)
It is worth pointing that many popular languages have Beaujolais
effect: e.g. the Borland Pascal "uses" clause, which takes an
additive, layer-after-layer, interpretation of what you see in the
used packages (units) definitely exhibits a Beaujolais effect.
Last time I looked at C++, my impression was that several years of
Beaujolais vintage productions would be required.
For component-based software development, such effects are undesirable
since your application may stop working when you recompile it with the
new -- supposedly improved -- version of a component.
8.5: What about the "Ripple Effect"?
(Tucker Taft explains)
We have eliminated all remnants of the Beaujolais Effect, but we did
debate various instances of the "Ripple" effect during the language
revision process (apologies to Gallo Ripple Wine enthusiasts ;-).
In brief, the (undesirable) Ripple effect was related to whether the
legality of a compilation unit could be affected by adding or removing
an otherwise unneeded "with" clause on some compilation unit on which
the unit depended, directly or indirectly.
This issue came up at least twice. One when we were considering rules
relating to use of attributes like 'Address. In Ada 83 as interpreted
by the ARG, if a compilation unit contains a use of 'Address, then
there must be a "with" of package System somewhere in the set of
library unit specs "with"ed by the compilation unit (directly or
indirectly).
In Ada 9X, we have eliminated this rule, as it was for some compilers
an unnecessary implementation burden, and didn't really provide any
value to the user (if anything, it created some confusion). The rule
now is that the use of an attibute that returns a value of some
particular type makes the compilation unit semantically dependent on
the library unit in which the type is declared (whether or not it is
"with"ed).
The second place the Ripple effect came up was when we were trying to
provide automatic direct visibility to (primitive) operators.
Ultimately we ended up with an explicit "use type" clause for making
operators directly visible. For a while we considered various rules
that would make all primitive operators directly visible; some of the
rules considered created the undesirable "Ripple" effects; others
created annoying incompatibilities; all were quite tricky to implement
correctly and efficiently.
_________________________________________________________________
9: Ada and Other Programming Languages
9.1: Where can I find programs that will translate from (some language) to
Ada?
(Job Honig)
Probably not the answer you like to hear, but my advice would be to
redesign the code, employing your knowledge of the current system, of
course. I have done this twice, once for Coco, a parser generator for
LALR left attributed grammars, and once for Flex, the well known
scanner generator. Both attempts revealed errors in the original
software, that were uncovered by designing the new system using the
higher abstraction level allowed by Ada...
So I would support your requirements analysis (transition to Ada), but
not your proposed implementation (using a source code translator).
(no longer Job Honig :-)
Otherwise, it is generally advisable to simply interface to code that
already works. Still, you may have compelling reasons to translate
your existing source to Ada. In that case, here is a list of available
translators:
* Pascal to Ada:
To see the differences in programming style, see "Ada for
Experienced Programmers", by A. Nico Habermann and Dewayne E.
Perry (Addison-Wesley Pub. Co., Reading, Mass., 1983). Covers Ada
and Pascal.
* Fortran to Ada: ???
* COBOL to Ada: ???
* C++ to Ada: ???
* C to Ada: ???
* Modula-2 to Ada:
(from Wayne R. Lawton)
The Idaho National Engineering Laboratory (INEL), a Dept of Energy
Lab has a basic capability for Modula-2 to Ada-83. The tool is
"research grade" quality, but may provide a starting point for
what you need. This is the same group of people who brought you
AdaSAGE. Give them a ring at (208) 526-0656. This is an answer
desk hotline in the section that wrote the tool.
If you are looking for commercial quality, I wish you the best of
luck. If you just need something to perform 80% of the grunt code
translation, I think this might meet your needs. I know of two
systems comprising about 250,000 lines of code that were
originally developed in Modula-2 then translated and cleaned up in
Ada 83 after Alsys 1.0 for the PC came out back around 1987.
* Visual Basic to Ada: NOT! :-)
9.2: How can I convert Ada 83 sources to Ada 9X?
First you should read the following document, which will provide you
with much useful information: "Changes to Ada -- 1987 to 1995", file
ch83.{ps,doc}, in directory
ftp://ajpo.sei.cmu.edu/public/ada9x/mrtcomments/rm9x/v5.95
If you're using GNAT, the tool you are probably looking for is
"gnatchop". In csh you could use something like this to quickly
process existing files:
cd dest_dir # The destination directory
foreach f ( ../src_dir/*.a ) # ../src_dir is the source directory
gnatchop $f
end
gnatchop will show you what sources are causing problems.
9.3: I hear that Ada is slower than Fortran or C, is that true?
First, note that you are comparing compilers, not languages. There is
no such thing as "fast" Ada code any more than there is "fast" C++ or
Fortran code. Now, when comparing execution speeds on similar
platforms, you must keep in mind the optimization levels, OS tuning,
etc. while making the comparisons. The bottom line is that
benchmarking, especially between two different languages, requires
_very_ careful measurement. In general, such results should be viewed
with caution.
(A message from Bevin Brett of DEC)
I have been asked to comment on the relative performance of algorithms
coded in Ada and in Fortran.
This question has come up repeatedly over the years, and deserves a
complete answer, rather than a simplistic one.
There are many factors which influence the size and execution speed of
the running program, and they all play together to get a full answer.
I shall then discuss an exact Ada v. Fortran comparison that Digital
was involved in.
First, a position statement: The variation between Ada and Fortran is
less than the variation within the language caused by the exact
implementation details. A person versed in the Ada issues should do as
well in Ada as a person versed in the Fortran issues will do in
Fortran. The size and execution speed of the result should be within a
few percent of each other.
(a) Differences due to the compiler
In the case of the DEC Ada and Fortran compilers, the optimizer and
code generator are the same. Never-the-less, the exact inputs into
the optimizer and code generator may differ slightly when the same
algorithm is compiled by the Ada and Fortran compilers, and this
can result in major differences in the generated code. In these
cases the compiler front ends can usually be modified to correct
the slower one.
We have not observed any major differences in generated code
quality between the DEC Ada and DEC Fortran compilers caused by
such issues.
(b) Differences due to the language
It is very important that the same algorithm be written in the two
languages. The biggest differences we have observed are
1. Having the wrong dimension varying fastest, since it is
desireable to have the first dimension changing fastest in
Fortran, and the last dimension in Ada. Thus when an
algorithm is transliterated, the array indexes must be
reversed.
2. Using compile-time-known bounds for arrays in Fortran, and
using unconstrained arrays in the Ada code. Knowing the exact
values of the dimensions at compile-time results in much
better code.
3. Not suppressing all the runtime checks in Ada. The Fortran
compiler assumes all array bounds are in range, and all
arithmetic operations do not overflow. You must use a pragma
Suppress to tell this to the Ada compiler as well.
4. Don't use arrays of Ada Booleans to match arrays of Fortran
Integers, because accessing bytes on a RISC system might be
much worse than accessing fullwords.
(c) Differences due to the bindings
The biggest bindings differences are related to Fortran's built-in
support for complex types, and for various math routines such as
SQRT and SIN, compared with Ada code that often uses hand-coded or
ISO standardised versions of these functions with different
requirements than are imposed on the Fortran versions.
DEC Ada has built-in support for complex types, and also has
bindings directly to the same primitives that Fortran uses for its
math routines and so gets the same performance as Fortran does.
(d) Differences due to the author
The use of good Ada and Fortran style can also effect the generated
code. Provided the author writes in good Ada style, and follows
the above guidelines, the generated code should do as well as
Fortran.
The Ada Performance Benchmark
A DEC Ada customer had a Fortran benchmark that had been translated
into Ada without awareness of the above issues, and was running
substantially slower with DEC Ada than the original was with DEC
Fortran.
Bevin Brett, a DEC Ada team member, developed the above guidelines in
the process of retranslating the code into Ada.
Portions of this translation are shown here (a) as an illustration of
the application of the above rules, and (b) as an illustration of the
kind of operations that were present in the benchmark.
The whole benchmark has not been provided to avoid possible issues of
ownership.
The resulting Ada benchmark components each ran within a few percent
of their Fortran counterparts. The Ada code is available by FTP, in
file ftp://lglftp.epfl.ch/pub/Ada/FAQ/ada-vs-fortran.ada
9.4: Isn't Ada less "elegant" than Eiffel?
While it is true that programming-language support for "assertions"
is an important contribution of Eiffel to software construction, this
is not an issue of "elegance", and there are many other important
factors to consider.
Note also that preconditions and postconditions can be fairly easily
and efficiently included in Ada code. Invariants seem difficult to
emulate directly in Ada. If you're really interested in the formal use
of assertions with Ada, maybe Anna is a solution for you.
(Tucker Taft comments)
I guess one thing that bothers me a little is that people are quick to
say that Eiffel is "elegant" without really looking at it. I fear that
such statements will become self-fulfilling prophecies, with those
programmers interested in elegance migrating over to Eiffel rather
than sticking with Ada.
In particular, although I like the assertion stuff in Eiffel, I think
the language has a number of "inelegant" aspects. For example:
1. exception handlers only at the top level of a routine, with the
only way to "handle" an exception being by retrying the whole
routine.
2. No way to return from a routine in the middle. This makes it a
pain in the neck to search through a list for something in a loop,
and then return immediately when you find what you want. (I have
never found the addition of extra boolean control variable a help
to the understanding of an algorithm.)
3. Namespace control handled by a separate sublanguage, and no real
higher level concept of "module" or "subsystem."
4. An obscure notation like "!!" being used for an important and
frequent operation (construction).
5. No way to conveniently "use" another abstraction without
inheriting from it.
6. No strong distinctions between integer types used for array
indexing.
7. Using the same operator ":=" for both (aliasing) pointer
assignment, and for value assignment, depending on whether the
type is "expanded." (Simula's solution was far preferable, IMHO).
And most critically:
8. No separate interface for an abstraction. You can view a interface
by running a tool, but this misses completely the importance of
having a physical module that represents the interface, and acts
as a contract between the specifier or user of an abstraction and
its implementor. In Eiffel, one might not even be truly aware when
one is changing the interface to an abstraction, because there is
no particular physical separation between interface and
implementation.
I consider many of the above problems quite serious, with some of them
being real throwbacks to the old style of programming languages where
there were no well defined interfaces or modules.
Hence, I cringe a bit when people say that Eiffel is the "most
elegant" OOP and that they would use it if only it were practical to
do so. In many ways, I think Ada is much better human-engineered than
Eiffel, with important things like range constraints built into the
language in a way that makes them convenient to use. Although general
assertions are nice, they don't give you the kind of line-by-line
consistency checks that Ada can give you.
To summarize --
Although Eiffel certainly has a number of nice features, I don't
consider it ready for prime time as far as building and maintaining
large systems with large numbers of programmers. And from a human
engineering point of view, I think Ada is significantly better.
9.5: Are there any papers detailing the differences between Ada and C++?
Below are two references. Bear in mind that it is difficult to make
such a comparison without exposing biases. However, the two papers
below are well worth reading.
"A Comparison of the OO features of Ada9x and C++" in Springer Lecture
Notes in CS: "Ada Europe 93" pp.125-141 (short paper, good reading,
enlightens idioms)
ftp ajpo.sei.cmu.edu in directory: /public/ada9x, document:
9x_cplus.hlp
next reply other threads:[~1995-01-19 18:10 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
1995-01-19 18:10 Magnus Kempe [this message]
-- strict thread matches above, loose matches on Subject: below --
1995-03-21 18:10 Ada FAQ: Programming with Ada (part 2 of 3) Magnus Kempe
1995-04-20 0:00 Magnus Kempe
replies disabled
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox