comp.lang.ada
 help / color / mirror / Atom feed
From: mheaney@ni.net (Matthew Heaney)
Subject: Re: Syntax for tagged record types
Date: 1997/05/25
Date: 1997-05-25T00:00:00+00:00	[thread overview]
Message-ID: <mheaney-ya023680002505971422590001@news.ni.net> (raw)
In-Reply-To: dewar.864485829@merv


In article <dewar.864485829@merv>, dewar@merv.cs.nyu.edu (Robert Dewar) wrote:

><<I think that using discriminants as "constructor arguments" is a real
>kludge, and obfuscates declarations.  Ada 95 doesn't have constructors, so
>let's not try to fight the language.>>
>
>Most people find the use of discriminants as "constructor arguments" a
>natural and powerful feature in Ada (though naturally they don't think
>in terms of this terminology).
>
>You can declare that "I think X is a kludge" for any X, but such a statement
>is only helpful to discussion if you give some idea *why* you think this.

I say this because "discriminant" should mean "tag in a disjoint union." 
Records are Cartesian product, and discriminated records are disjoint
union.  And that's all discriminate record should mean.

But "tag of a disjoint union" is a far different thing than "arguments to a
constructor."  And when you use discriminants for the latter, the
declaration is ambiguous, and I the maintanence programmer have to do work
to figure out what you mean.

It's not unlike another sin commited by many Ada programmers.  That's when
you use "and then" in predicates, when you really mean "and."  Many Ada
programmers do this in a naive attempt at some sort of optimization, but it
should only be used when you want to enforce an order dependence on
evaluation of the parts of the predicate.

Now when I see a predicate with "and then," it's ambiguous, because I don't
know whether you're making (an unnecessary) optimization, or whether there
really is an order dependence.  You, the writer of that code, are being
unthoughtful to me, the maintainer of that code, because you have abrogated
your responsibility to tell me what you mean explicitly and unambiguously,
and therefore I am put in the unwanted position of having to guess.

That Ada evaluates all the parts of a predicate, even if one early part
evaluates to false (say, in a complex Boolean expression with more than one
"and"), is an _essential_ characteristic of the language.  When a
programmer uses "and then" everywhere, he is really wishing that the
language were some other way, and is desperately trying to somehow "fix"
the language.

But this is no fix at all.  And I the poor maintanence programmer am the
victim of your thoughtlessness.

The language is the way it is.  Accept that.  If it doesn't do exactly what
you want, stay within the spirit of the language anyway, and justify it by
realizing that Ada gives you a lot a things you wouldn't have if you were
programming in another language, so you're still operating in the red.

Respecting the spirit of the language means not using a discriminant as a
constructor parameter.  Norm stated in his book that "In Ada, the role of
these [constructor] parameters can be played by discriminants." (p. 579) 
Now, I like Norm, and I thank him a thousand times over every time I can
pick up his book and answer a question, but this is horrible advice.  When
you use a discriminant as a constructor parameter, the purpose of the
discriminant is now ambiguous.  And I the poor maintanence programmer have
to figure out what you mean.  So I have to guess, and I might guess wrong.

Respecting the spirit of the language means not using short-circuit control
forms as an optimization.  When you do that, the short-circuit form is
ambiguous, and I have to figure out whether you meant "this is an
optimization" or "this is an order-of-evaluation dependency."  I have to
guess what you mean, and I might guess wrong.

It's not unlike Ada 83 programmers who, in their zeal to "repair" Ada,
"simulated" inheritance hierarchies.  I can tell you this causes nothing
but obfuscation of the code, and any supposed benefits of "reusability" or
"being more object-oriented" were more than offset by the attendent
complexity of trying to use a solution not directly supported in the
language.

The whole point of software engineering is to _minimize_ complexity.  You
must be _unrelenting_ in seeking out techniques to _simplify_ the software. 
So when you make your code more complex by "simulating" inheritance, in
some sort of quixotic attempt to be "more object-oriented" - because you've
been led to believe that "more" object-oriented is "better" - then I have
to ask myself what kind of engineering this is.

Ada 83 is not object-oriented.  Accept this.  Use the tools provided by the
language _as_is_.  Because when you try to bend the language to "repair"
it, you distort the clarity of your solution too, because it's expressed in
a way that isn't natural for the language.  And when your solution isn't
crystal clear, then I the maintenance programmer have to figure out what
you mean.  I don't want to figure out what you mean; I want you to tell me,
unambiguously.

This doesn't mean being object-oriented isn't good.  It's just that Ada 83
doesn't have inheritance (technically, "type extension") or polymorphism. 
So respect that "limitation."  Rationalize this by saying to yourself that
the language has many other good features that aren't in other languages,
so you're still ahead in the game.

One nascent (maybe not-so-nascent) phenonmenon that I've observed is a
dissatisfaction with the fact that, in Ada, a module is not a type, "you
know, like in other languages."  I've seen programmers name their abstract
data type "object" or "instance," so that the "real" name is only obtained
by qualifying the type name with the package name, as in

   The_Stack : Stack.Object;

This is a horrible naming convention.  It represents a desperate attempt to
repair the language, by trying to force the package to be a type.

What are you going to name the iterator for the stack?  "Object" has
already been used within that namespace.  What you'll probably do is create
a child package, so you can again name your iterator type
Stack.Iterator.Object.  This too is a mistake: the point is that the
language was _designed_ so that you can declare closely related abstract
data types _together_ in the _same_ package.

This proliferation of packages - as many packages as there are ADTs - is a
complete mistake, and is orthogonal to the intended usage of the language. 
The whole point is to prevent getting lost in a sea of types (as you do in
other languages, where the module is a type), by using packages as a
higher-level organizing principle.  If you have as many packages as types,
then what have you gained by having packages at all?

In Ada, a package is a module.  It is not a type.  It is a language tool to
give the programmer explicit control of namespace - nothing more, and
nothing less.  Accept this, and respect the language, by giving the type
itself the name of the abstraction.  Respect the intended usage of Ada by
putting the data structure ADT and its active iterator ADT togther in the
same package.

Yes, I know this isn't how it is in other languages, but rationalize it by
saying that there are plenty of things you do have in Ada that you don't
have in other languages.  And plently things you don't have to do. 
(Example: In Ada, you don't need a "friend" construct, because visibility
is implied by the colocation of types in the same package.)

It's not unlike the German language.  Those silly Germans, they put all the
verbs at the end of the sentance.  So let's all try to "fix" German, but
putting all the verbs where they "should" be.

Obviously, this would be horrible thing to do.  That verbs go at the end of
the sentance is an _essential_ characteristic of the German language. 
Somehow, the Germans manage to speak with each other just fine, thank you
very much.  Imagine that!

You must think in the paradigm of the computer language to effect the most
elegant solution to a problem.  It does no one any good (least of all the
poor maintenance programmer) when you fight Ada by trying to incorporate
"better" features (say, automatic short-circuiting) found in other
languages, without those features being directly supported.

Here's a quote from Jean Ichbiah, on the nature of design: "One can only
reach a harmonious integration of several features by immersing oneself
into the logic of the existing parts."

It means that the language is the way it is, and you must immerse yourself
in the language, warts and all, to achieve beauty and harmony.  So when I
say using discriminants as a constructor is a "kludge," I'm saying that
you're fighting the language.  Ada 95 doesn't have constructors.  That is
(for now) an essential characteristic of the language.  Use discriminants
as discriminants (that is, as the tag in a disjoint union), and accept that
you don't have constructors, and rationalize this by realizing that the
language is pretty good even without them.

--------------------------------------------------------------------
Matthew Heaney
Software Development Consultant
<mailto:matthew_heaney@acm.org>
(818) 985-1271




  reply	other threads:[~1997-05-25  0:00 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
1997-05-24  0:00 Syntax for tagged record types Robert Dewar
1997-05-25  0:00 ` Matthew Heaney [this message]
1997-05-26  0:00   ` Robert A Duff
1997-05-27  0:00   ` Jon S Anthony
1997-05-27  0:00 ` Dale Stanbrough
1997-05-27  0:00 ` Tucker Taft
replies disabled

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