[comp.lang.ada] Collective response to := messages

mendal@ANNA.STANFORD.EDU (Geoff Mendal) (11/29/88)

I will respond briefly to some of the replies made to my original
assignment overloading message.  Lines with ">>" are from my original
message, lines with ">" are responses to my original message.  I am a
bit shocked that some people apparantly agree with me... I'm not used
to that sort of thing.

>Date: 23 Nov 88 22:57:59 GMT
>From: hubcap!billwolf@gatech.edu  (William Thomas Wolfe,2847,)
>Subject: Ada Language Change: Assignment Overloading
>
>> 1. Why simply restrict it to assignment?  Why not include membership,
>>    short-circuit control forms, indexing, selection, attributes, etc.?
>>    These are all basic operations....
>
>   First, assignment is a "fundamental" operation; except for attributes,
>   the other things you've suggested apply only to specific classes of
>   ADTs.  As for attributes, I think we could live without them if 
>   appropriate procedures and functions were defined over the predefined
>   ADTs; having attributes for predefined ADTs and functions for user-defined
>   ADTs is another inconsistency.  Procedures and functions are definitely
>   necessary; the attribute mechanism is not.

Wrong.  Membership, qualification, attributes, and explicit type conversion
are all "fundamental" operations defined for EVERY type.  To argue that
assignment is special requires more justification than "we can live without
X and Y is necessary".  Who can live without X?  I can most certainly live
without assignment overloading.  Does this mean that overloading assignment
is a good/bad idea?  Nonsense.  Rethink your argument.

>   Another reason is that assignment procedures need to be invoked as part
>   of the evaluation of a user-defined ADT which is passed by value.  No
>   similarly vital function is performed by the other operations suggested.

I don't understand this at all.  You are assuming pass by value for an
ADT?  Rediculous.  The LRM only states that scalars and access values
are passed by copy.  In general, for an ADT, you cannot assume pass by
value and if your program depends on it, your program will execute
erroneously.  Justifying a language change based on a set of programs
which are known to execute erroneously it itself an erroneous argument.
If an "assignment procedure needs to be invoked as part of the evaluation
of a user-defined ADT" then one can explicitly write the Ada code
necessary for this.  Default components for records provide a simple
solution here.  There are other solutions as well.  None of the solutions
REQUIRE assignment to be overloaded.

>   The argument that the operations listed (except assignment and attributes)
>   should be user-programmable with respect to user-defined ADTs is reasonable,
>   but clearly assignment is in a higher class of importance than the others.

I strongly disagree.  Assignment is assignment.  A relational operator
is a relational operator.  Are we going to change the language by
taking a popular vote of which operations are "higher class" than
others?  Such justification for a language change is rediculous.  So
far, I have seen no justification why assignment should be overloaded
and not other basic operations.  Consider usability, readability,
understandibility, etc.  These should drive the justification, not
someone's arbitrary "higher class" argument.  My argument is that
while readability might be increased, understandability will be
diminished and therefore we should reject the notion that overloading
assignment is a good idea.  The designers of Ada went out of their way
to make typing Ada programs more difficult/time-consuming so that
understanding them would be easier.  Any language change should not
compromise understandability for the sole gain of making programs
easier to type.

>Date: 23 Nov 88 19:13:34 GMT
>From: trent@unix.sri.com  (Ray Trent)
>Subject: Ada Language Change: Assignment Overloading
[some verbage deleted]
>
>This standard argument against allowing the overloading of the ":="
>operator ignores the fact the programmers may want *semantics*
>in their type declarations instead of just syntax. Types are not 
>static objects that can be completely defined at compile time,
>however much the designers of Ada want to think they are.
[fraction example deleted]
>
>The Ada apolgists would have us write the semantics for ensuring
>LCD form for the type Fraction into *each and every operator*
>that acts upon 2 items of this type, instead of having that 
>functionality in the assignment operator, where it belongs. The point 
>is, readable or not, ":=" should validly ensure the *semantics* of
>every type it is defined for, as well as the pure syntactical elemants.
[more fraction examples deleted]

First, assignment is NOT an operator.  It is a basic operation.
Operators are completely different in that they follow different
scope/visbility rules and are afforded some luxeries (overloading)
not currently available to basic operations.  I tried to make this
point clear in my original message but it is obvious that still not
everyone understands the difference.  Read the LRM and be enlighted.

Now, of course programmers want semantics in their type declarations
and I believe that Ada provides some of these semantics through
predefined behavior of basic operations and operators.  But I disagree
that having the complete semantics for a type defined in an
"assignment operator" is appropriate or even desired.  Assignment
already has a very well defined semantics.  Read the LRM.  So what is
the problem?  You don't like those semantics and you would prefer to
alter them?  Fine, but I fail to see why ":=" has to be the syntagma which
carries your favorite semantics (which may not be my favorite
semantics).

One of the examples that I deleted above was "A := B / C;"
where A, B, and C were of some ADT (Fraction).  If I understand the
argument for placing the semantics in assignment instead of having the
programmer define the semantics in each user-defined operation, then
how would the semantics of passing the expression "B / C" to some
subprogram formal be handled?  There is no "assignment" here.  The
semantics have to be part of the division operation.  Perhaps you
are arguing that indeed "assignment semantics" should be a part of
EVERY operation.  This is a completely different language change,
far more involved than a "simple" overloading of "assignment".
Perhaps those who are involved with Ada formal definition projects
(DDC, etc.) can shed some light on what is involved here and if
such would be useful.

>The argument that pre-processors can be used to acheive this effect is
>bull. It ignores the absolute fact that *no one* maintains, reads, or
>is expected to understand the processed code. This is a null argument.

Quite the contrary.  The whole idea of a pre-processor or translation
tool is to remove the need to "read" the processed version.  How many
Ada programmers spend their time disassembling and reading the output
of their compilations?  A compiler is a translation tool.  The idea
being that Ada is more readable/understandable/etc. than assembly.  So
why is it that defining a mapping from some pre-processor to Ada is
met with such cynicism?  It simply provides an additional layer of
abstraction, which, the last time I looked, was thought to be a good
idea.

Those who are convinced that there is nothing above/beyond Ada should
continue to keep their blinders on so that they won't have to worry
about keeping up with computer science and software engineering.  If
others in our field had adopted this attitude, we'd still be
programming by wire wrapping and debugging with oscilloscopes.  (It is
a sad commentary that there are still Ada programmers out there who
still do not understand the need for and utility of an APSE.  You can
spot them right away... they're the ones who think that the only way
to improve/change things is by modifying the language.)

>Date: 24 Nov 88 01:55:31 GMT
>From: hp-sdd!ncr-sd!ncrcae!hubcap!billwolf@hplabs.hp.com  (William Thomas Wolfe,2847,)
>Subject: Ada Language Change: Assignment Overloading
>
>   Why can't an overloadable operation remain as a basic operation?
>
>   In order to preserve compatibility, the "old" interpretation of
>   assignment could be kept as the outermost interpretation (deepest
>   in the background), available as a default assignment procedure.

Yech!  The current language definition is clear and consistent.  A
basic operation (such as assignment) cannot be overloaded.
Overloading is defined only for subprograms, single entries,
operators, and enumeration literals.  My point was that if assignment
was to be overloaded, then it would most likely have to be
reclassified as an operator or a procedure.  Otherwise, this language
change would require much more work.  Having assignment as the only
basic operation which could be overloaded makes the semantics of
"assignment" more murky.  Overloading is complex enough without adding
a new special case to it.  And for what benefit?  I still haven't seen
a response that illustrates a useful case where overloading assignment
would do anything useful.  Does anyone have such a case?  Or is this
basically a theoretical argument whereby the impact of a language
change will not be understood until it has been made and programmers
start to experiment with it?

>>    Program provers might rely on the fact that if no exception is raised 
>>    during the "assignment", that the the value is assigned to the object.  
>
>   And what about the overloadable relational operators???
>
>   Program provers cannot now assume that "=" really means equality,
>   nor can they assume that some side effect will not occur during the
>   evaluation of an equality operator.  There is plenty of precedent here.
>   If we go ahead and overload assignment, the program prover will be able
>   to exercise one simple, consistent rule: "Assume nothing".  I think this
>   is an excellent mode of thought to force a program prover into.  

Well, not really.  Since "=" can only be overloaded by the user for
limited types, it is not a good example.  "*" is a better example.
But the point is that the current semantics of assignment do allow
program provers to make certain assumptions which make the job of
writing a program prover easier.  While it is certainly possible to
write a program prover which does not make use of the current
assignment semantics, it is far harder to do so.  The point being that
there exist applications of Ada out there which will suffer somewhat
from this proposed language change.  Whether this is a desired result
is debatable (do we really want to force the program proving folks to
go back and undertake a major redesign because a few programmers hate
to type ASSIGN?).

>> The proponents of overloading assignment have also overlooked other
>> less costly solutions such as pre-processors that would transform
>> "overloaded assignment" into Ada procedure calls, implementation-defined
>> pragmas, etc.
>
>    Wrong.  The ability to cleanly define abstract data types 
>    is very much a language issue.  Implementation-defined pragmas
>    would annihilate portability.  Preprocessors would have one hell
>    of a time implementing "If this procedure call involves passing
>    a user-defined ADT by value, then generate a temporary variable
>    of that type, invoke ASSIGN on that temporary (assuming we adhered
>    to the convention of using the name ASSIGN rather than, for example,
>    COPY or DUPLICATE), and pass the temporary instead, and remember 
>    to invoke DESTROY on the temporary afterward"...
>
>    There's no way around it.  Assignment needs to be overloadable.

I don't understand this argument at all.  Clearly one can write ASSIGN
procedures by hand to solve this sort of thing.  The only difference
is that ASSIGN (X, Y) has to be written instead of "infix" X := Y.
So why does assignment need to be overloaded?  And if one can write
the code with the intended semantics by hand, then why cannot an
advanced APSE tool do so?  Is your argument that the state of APSE
technology is not sufficient to solve the problem, and therefore we
have to solve it as a language problem?  This is again a silly argument
for making a language change and would prove my case that this is
indeed an APSE issue, not a language issue.

***********
Since overloading of assignment is not a very important issue, I'll
refrain from making any further comments about it.  I'll get back to
doing more important things... writing pre-processor tools for Ada.

gom

dd@sei.cmu.edu (Dennis Doubleday) (11/29/88)

In article <8811282217.AA04896@ajpo.sei.cmu.edu> mendal@ANNA.STANFORD.EDU (Geoff Mendal) writes:
>But the point is that the current semantics of assignment do allow
>program provers to make certain assumptions which make the job of
>writing a program prover easier.  While it is certainly possible to
>write a program prover which does not make use of the current
>assignment semantics, it is far harder to do so.  The point being that
>there exist applications of Ada out there which will suffer somewhat
>from this proposed language change.  Whether this is a desired result
>is debatable (do we really want to force the program proving folks to
>go back and undertake a major redesign because a few programmers hate
>to type ASSIGN?).

I agree that overloading of ":=" is a bad idea, but your line of
reasoning here is not convincing (to me).  I would change your last
question to "are we really going to limit programmers to only those
language constructs which the program proving folks find easy to
handle?".  If the answer is "yes", then we're not going to get much
real work done for the next decade, at least.

Dennis Doubleday                       dd@sei.cmu.edu
Software Engineering Institute         (412)268-5873
Carnegie Mellon University
Pittsburgh, PA 15213

trent@unix.SRI.COM (Ray Trent) (11/30/88)

In an article mendal@ANNA.STANFORD.EDU (Geoff Mendal) writes:
>>The Ada apolgists would have us write the semantics for ensuring
>>LCD form for the type Fraction into *each and every operator*
>>that acts upon 2 items of this type, instead of having that 
>
>First, assignment is NOT an operator.  It is a basic operation.

The argument is that it should be. (and is, in any sensible 
(i.e. non-Ada) definition of the word operator) That there is no
reason to have "basic" operators that are "special".

>One of the examples that I deleted above was "A := B / C;"
>where A, B, and C were of some ADT (Fraction).  If I understand the
>argument for placing the semantics in assignment instead of having the
>programmer define the semantics in each user-defined operation, then
>how would the semantics of passing the expression "B / C" to some
>subprogram formal be handled?  There is no "assignment" here.  The

Fine, perhaps a better example is assigning to an object that should 
be garbage-collected before assignment. (always, or the program 
will be erroneous (not in the Ada sense)) Which makes more sense, 
putting garbage collection in assignment, or assuming the programmer 
will always call a garbage collector while assigning? Even if
the programmer is a code maintainer, and not the designer?

If you are wondering why I chose my original example, consider
A := (B / C * D) + ... + (Q / (3/4)). (all variables are of type fraction,
and an appropriate "/"(A,B : INTEGER) is defined) Do you still want
LCD maintainance in the arithmetic operations? Of course, you could 
always force A := LCD(foo). What if someone wants to modify the program? 
Aren't they going to wonder/forget about this? If you are worried
about passing the expression B / C as a parameter, my answer is: 
if you have a procedure where LCDness is necessary for proving something,
you can *always* simply make "in" parameters "in out" parameters. This 
*ensures* that an input variable will be in LCD form. (if assignment is
overloadable and is overloaded as suggested) "Requiring" a A := LCD(B/C)
does not ensure the validity of something passed to a sub-program.

If assignment were overloadable, the writer of an Ada spec for
operations involving fractions could be *sure* of always having something
in LCD form when passed to a subprogram by overloading ":=" and passing
all variables as "in out" parameters. The only other way to do this
is force *all* subprograms to perform LCDing on their outputs (or inputs)
whether that condition is necessary or not. This would make the operation
A := <very complicated expression with *many* arithmetic operations>
extremely slow (potentially). The LRM explicitly says that intermediate
results need not follow the constraints of the ADT. 

>being that Ada is more readable/understandable/etc. than assembly.  So
>why is it that defining a mapping from some pre-processor to Ada is
>met with such cynicism?  It simply provides an additional layer of

Because it is used to justify inconsistent language constructs. If 
I had a nickel for every time someone said, "you don't need to 
change the language, just use a pre-processor" I'd drown in them.
Point one: any such preprocessor needs to be standardized to the
level that the language is, or it defeats the purpose of the language.
Point two: If such a preprocessor *is* in fact standarized to the level that 
the language is, there is no discernable difference between changing
the language and adding a preprocessor. Point three: the argument is that 
assignment should not be overloadable because it is not understandable
(though more readable). It makes no real difference whether you put
assignment overloading into a pre-processor or into the language. 
The pre-processing source is what will be maintained and used. 
It is identically as understandable if the language is changed as
it is if a processing step is added. Perhaps more so. If the language
is changed, no one will *assume* that assignment cannot be overloaded.
Tacking on a preprocessor is the coward's way out.

>a sad commentary that there are still Ada programmers out there who
>still do not understand the need for and utility of an APSE.  You can

It's needed because the language was designed in an inconsistant and 
inflexible manner in the first place. What would you say about a 
preprocessor that converts C into Ada. Is programming in C then
as good as programming in Ada? (don't laugh, it can be done, especially
if you use unchecked programming) I don't think so.

Another change I'd like to see. (upward compatible this time) Allow:

  if A in B | C | D | Q | Y | N then
    ...
  end if;

I.e. an extension of the membership idea. It's ridiculous to me that

  case A is 
    when B | C => ...
  end case;

is valid but the above (or some equivilent) is not. I argue that such
a construct is both more readable and more understandable than
 
  if A = B or A = C or else ... or else A = N then
    ...
  end if;

and also makes the Ada language more internally consistent. (and thus
easier to use) I also argue that compiler vendors are likely to 
implement the former more efficiently than the latter. This is especially
true when the comparands are of a large enumerated type. (hashing, and all)

Anyway, enough diatribe for today.
-- 
"Our little lives get complicated
 It's a simple thing
 Simple as a flower
 And that's a complicated thing"                     ../ray\..

billwolf@hubcap.clemson.edu (William Thomas Wolfe,2847,) (11/30/88)

From article <8811282217.AA04896@ajpo.sei.cmu.edu>, by mendal@ANNA.STANFORD.EDU (Geoff Mendal):
>>   Another reason is that assignment procedures need to be invoked as part
>>   of the evaluation of a user-defined ADT which is passed by value.  No
>>   similarly vital function is performed by the other operations suggested.
> 
> I don't understand this at all.  You are assuming pass by value for an
> ADT?  Rediculous.  The LRM only states that scalars and access values
> are passed by copy.  In general, for an ADT, you cannot assume pass by
> value and if your program depends on it, your program will execute
> erroneously.  

    This is precisely the problem.  This is what must be fixed.

> [Argues that the proposal would mean more work for language designers]
>
> [Argues that the proposal would mean more work for program provers]
>
> [Argues that the proposal would mean less work for him, since he
>  is employed as a writer of preprocessor tools for Ada]

     Much more money is spent using a programming language such as Ada
     than is spent designing it.  Hence, if we can save the time of
     30 million programmers at the expense of the time of 300 language
     designers, I submit that this is an appropriate tradeoff.  

     What is needed is a completion of the ADT paradigm, support for
     which is the fundamental reason for the "limited private" feature.
     Given that one must remember to destroy all non-predefined ADTs 
     upon block exit, it is clear that the implications of this paradigm 
     were not appropriately considered in the Ada 83 design. 

leake@cme-durer.ARPA (Stephe Leake) (11/30/88)

There seems to be a point of confusion concerning overloading of ":=".
Some people assume that the (overloaded) semantics of ":=" apply to
parameter passing. This seems to be a reasonable assumption, and I
think the current Ada parameter passing semantics are equivalent to
the current Ada assignment semantics. However, other types of
overloading (such as for "*") only apply when the overloaded symbol
appears _explicitly_; thus an argument could be made that the
overloaded ":=" should only be used for explicit assignments, not the
implicit ones in parameter passing. Personally, I think parameter
passing should be the same as assignment, but I just wanted to point
out the confusion.

In Ray Trent's discussion of the Fraction Abstract Data Type, he
implies that "*" and "/" do not reduce fractions to LCD form, but ":="
does. It seems to me, therefore, that he is talking about 2 data
types; LCD_Fractions and Fractions. This removes the need to overload
":=". "*" and "/" take either type and return Fraction. Then, since
there are two types involved, the compiler will remind maintainers to
include the explicit LCD operation;

A : LCD_Fraction;
B, C : Fraction;

A := B /C ; 	-- illegal
A := To_LCD_Fraction (B / C);	-- legal

I don't think ":=" should be overloadable just so we can hide type
conversions; this is one of the main complaints about C - one can
never be _sure_ what type something is.

The example above brings up a suggestion; type names should be
overloadable by functions, so we can define type conversion functions
that look like the type conversions available to scalar types. Ie the
function "To_LCD_Fraction" above should be named "LCD_Fraction". I
don't know which section of the LRM to refer to here; overloading
rules seem very scattered.

Ray Trent says:
	> The LRM explicitly says that intermediate
	> results need not follow the constraints of the ADT.
Where does it say this?

I second the motion for allowing:

  if A in (B | C | D | Q | Y | N) then
    ...
  end if;

(note that I added parentheses). Currently, we can get the same effect
with a subtype, but _only_ if B | C | D | Q | Y | N can be _uniquely_
described by a range constraint, which is often not the case. This is
leaning further towards defining sets in Ada, something that PASCAL
has always had. There are certainly problems with fully implementing
sets, but I think this extension is a good one.

Stephe Leake 	(301) 975-3431 		leake@cme.nbs.gov
National Institute of Standards and Technology
(formerly National Bureau of Standards)
Rm. B-124, Bldg. 220
Gaithersburg, MD  20899

dsr@hector.UUCP (David S. Rosenblum) (12/01/88)

In article <24856@sri-unix.SRI.COM> trent@unix.sri.com (Ray Trent) writes:
>In an article mendal@ANNA.STANFORD.EDU (Geoff Mendal) writes:
>>
>>First, assignment is NOT an operator.  It is a basic operation.
>
>The argument is that it should be. (and is, in any sensible 
>(i.e. non-Ada) definition of the word operator) That there is no
>reason to have "basic" operators that are "special".

Most of the non-Ada definitions of "operator" that I am familiar with
describe operators in a purely mathematical sense, i.e. in terms of
domains and algebras.  However, assignment is a concept that is peculiar to
programming languages, so in a sense you're comparing apples and oranges.
And most "sensible" definitions of assignment that I know of describe
assignment simply in terms of placing a value in a variable or memory location.

Basic operations in Ada ARE special, for very good reasons.  Unlike operators,
they are intimately connected with Ada's model of type constraints in some
way and cannot be hidden.  One of the nice features of Ada type constraints is
that they are (or at least were intended to be) implemented consistently
throughout the language, in part via the basic operations.  This consistent
enforcement would be lost if overloading of basic operations were allowed.  In
particular, by allowing overloading of assignment, it would be possible to
override one of the fundamental steps of the assignment operation, which is to
guarantee that the target variable in an assignment statement is assigned a
legal value of its type.  When you overload operators such as "=" and "+",
you may give perverse semantics to operators that have a "traditional"
meaning, but you are not able to override Ada's type checking.  Thus,
allowing overloading of assignment (or any basic operation) would seriously
weaken Ada's strong typing features.

>[lots of discussion about LCD deleted]

As I understand your LCD example, you seem to see assignment overloading
as a vehicle for implementing type constraints that can't be expressed in
Ada's constraint language (such as LCD-ness).  So why stop with assignment?
Wouldn't you need overloading of all the basic operations?  Suppose
I'm given a language change which allows overloading of basic operations,
and suppose I have a type EVEN, e.g.,

	type EVEN is new INTEGER;

which I want to constrain in a way that would require all variables of
the type to have even values.  In order to enforce this constraint
consistently in the manner of Ada, I would want to do the following:

(1) Overload := so that it raises CONSTRAINT_ERROR when given an odd
    right-hand-side.
(2) Overload the attributes 'SUCC and 'PRED so that they skip odd values.
(3) Overload the attributes 'POS and 'VAL so that count only even values.
(4) Overload the attribute 'FIRST so that it returns the smallest even
    value of EVEN's base type.
(5) Overload the attribute 'LAST so that it returns the largest even
    value of EVEN's base type.
(6) Overload the membership tests and the ".." token so that only
    even ranges are considered.
(7) Overload the indexing operation for all array types I define that are
    indexed by EVEN values, so that even-numbered components are stored and
    accessed contiguously.
(8) Overload the aggregate operation for such array types so that
    the index values for the aggregate are computed "the right way".

Yet after all these exertions, my evenness constraint would STILL not
be enforced consistently, unless I was given several more language changes.
For example, type conversions, qualifications, attribute evaluations and
membership tests never propagate exceptions.  Yet what would be the effect
of the type conversion "EVEN(3)"?  What would be the effect of the attribute
evaluation "EVEN'POS(3)"?  What would be the effect of the tests
"2 in 0 .. 3" or "3 in 0 .. 10" ?  Should they raise CONSTRAINT_ERROR, or
should they round 3 to some even value?  Suppose I instantiate INTEGER_IO to
perform I/O on EVEN values.  How do I get the instantiation to enforce my
evenness constraint?  First I would need another language change that requires
source-level assignment "operators" to be used to implement parameter
passing.  But I'm still in trouble because my overloaded := cannot be made
visible to my INTEGER_IO instantiation.  So I would ALSO need INTEGER_IO to be
redefined to accept a generic formal := parameter.  Oy!

I agree with Geoff.  The proponents of assignment overloading have not
addressed the ramifications of their proposal thoroughly enough to come
up with a fully consistent change to the language.  Until they do, I'm
happy to write ASSIGN instead of := whenever I need a "special" assignement
operation.


-- David

-------------------------------------------------------------------
David Rosenblum			UUCP: {ucbvax, decvax}!ulysses!dsr
AT&T Bell Laboratories		ARPA: dsr@ulysses.att.com
600 Mountain Ave.		      dsr%ulysses@att.arpa
Murray Hill, NJ 07974-2070
(201) 582-2906
-------------------------------------------------------------------

eachus@mitre-bedford.ARPA (Robert Eachus) (12/01/88)

     There has been a lot of frothing at the mouth on both sides of
this issue about what is or is not the Ada model of assignment.  First
of all a lot has been said about what a change to the language it
would be to make assignment or other basic operations overloadable.
However, if you look in 8.7 Context of Overload Resolution, the first
paragraph states:

     "Overloading is defined for subprograms, enumeration literals,
operators, and single entries, and also for the operations that are
inherent in several basic operations such as assignment, membership
tests, allocators, the literal null, aggregates, and string literals."

      Overloading, and overload resolution involving basic operations
is a fact of Ada life.  If you think otherwise try:

      package Overload_Test is
        type A is access Boolean;
        B: constant Boolean := null = null;
        type C is access Character;
        D: constant Boolean := null = null;
      end Overload_Test;

      The compiler should accept the decalartion of B, but reject the
declaration of D.  A lot of work went on during the language
definition process to make similar (but useful) cases work for numeric
types. If you object that this example involves equality which is not
a basic operation instead try:

       package Overload_Two is
	 A: Boolean := 'b' in 'a'..'c';
         type New_Char is ('b', 'a', 'c');
	 C: Boolean := 'b' in 'a'..'c';
       end Overload_Two;

billwolf@hubcap.clemson.edu (William Thomas Wolfe,2847,) (12/01/88)

From article <10906@ulysses.homer.nj.att.com>, by dsr@hector.UUCP (David S. Rosenblum):
> One of the nice features of Ada type constraints is
> that they are (or at least were intended to be) implemented consistently
> throughout the language, in part via the basic operations.  This consistent
> enforcement would be lost if overloading of basic operations were allowed.  In
> particular, by allowing overloading of assignment, it would be possible to
> override one of the fundamental steps of the assignment operation, which is to
> guarantee that the target variable in an assignment statement is assigned a
> legal value of its type.  

     Since the overloaded assignment would be implemented in terms of
     the existing low-level assignment operations, I don't see where any 
     potential problem exists here.  Please clarify.
 
> As I understand your LCD example, you seem to see assignment overloading
> as a vehicle for implementing type constraints that can't be expressed in
> Ada's constraint language (such as LCD-ness).  
> [...] 
> Yet after all these exertions, my evenness constraint would STILL not
> be enforced consistently, unless I was given several more language changes.
> For example, type conversions, qualifications, attribute evaluations and
> membership tests never propagate exceptions.  Yet what would be the effect
> of the type conversion "EVEN(3)"?  What would be the effect of the attribute
> evaluation "EVEN'POS(3)"?  What would be the effect of the tests
> "2 in 0 .. 3" or "3 in 0 .. 10" ?  Should they raise CONSTRAINT_ERROR, or
> should they round 3 to some even value?  

    The problems mentioned pertain specifically to this implementation
    only; if EVEN were implemented as a limited private type within an
    appropriate package, all the operations mentioned would be available 
    only through the procedures/functions provided.  Type conversions
    would not be legally expressible in the usual notation, most attributes
    would not be defined, etc.  Ada 83 allows us to do all of these things,
    but not in the "conventional" notation.  

    Since Ada does not allow implicit type conversions, the fact that we
    cannot overload type conversion does not cause us any grief in the
    parameter-passing process.  Nor do any of the other operations mentioned.

    Only assignment and destruction play vital roles in the evaluation of
    parameters and in the creation and destruction of variables (particularly
    with regard to block entry and exit).  It is the fact that the language
    does not consider the impact of these mechanisms upon the ADT paradigm,
    mechanisms which can play havoc with our abstractions, that compels us 
    to call for changes in the language with regard to the ability to
    define specific procedures for assignment and destruction which are 
    taken into consideration by these mechanisms. 

> I agree with Geoff.  The proponents of assignment overloading have not
> addressed the ramifications of their proposal thoroughly enough to come
> up with a fully consistent change to the language.  

    It is entirely possible that given the desire of AJPO to minimize the
    extent of changes in the language, it will not be possible to get Ada
    to provide complete support for the abstract data type paradigm.  Ada
    was explicitly intended as an interim solution, covering the range
    1983..20xx, and not as "the perfect language".  Perhaps for Ada it is
    already too late.  But the issue needs to be thoroughly discussed,
    because it is a major area in which Ada has "missed the boat". 

    Perhaps there is a solution which AJPO will find acceptable.  We must
    know whether or not this is the case.  If there is no hope for Ada,
    then we must focus on the construction of Ada's successor.


                                           Bill Wolfe

                                    wtwolfe@hubcap.clemson.edu

dsr@hector.UUCP (David S. Rosenblum) (12/01/88)

In article <3698@hubcap.UUCP> wtwolfe@hubcap.clemson.edu writes:
>From article <10906@ulysses.homer.nj.att.com>, by dsr@hector.UUCP (David S. Rosenblum):
>> In
>> particular, by allowing overloading of assignment, it would be possible to
>> override one of the fundamental steps of the assignment operation, which is to
>> guarantee that the target variable in an assignment statement is assigned a
>> legal value of its type.  
>
>     Since the overloaded assignment would be implemented in terms of
>     the existing low-level assignment operations, I don't see where any 
>     potential problem exists here.  Please clarify.

Essentially, the problems arise from the characteristics of Ada's parameter passing semantics.
(Or we could change those too while we're at it. :-) )  Suppose ":=" overloadings are required
to have an out-mode parameter to represent the target variable.  Then an overloaded ":="
on a composite type can selectively assign to some components of the target and
leave other components undefined (if they were undefined prior to execution of the assignment).
This cannot happen with predefined assignment to objects of a composite type (i.e., when a full
composite object, not just a component of one, is given as the left-hand side).  Alternatively,
suppose ":=" overloadings are required to have an in out-mode parameter to represent the target
variable.  Then it would be impossible to assign to an undefined variable, because the
variable would first be constraint-checked when it is passed in to the assignment procedure.

For example,

procedure CIRCUMVENT is

    type LCD_FORM is
	record
	    NUMERATOR, DENOMINATOR : INTEGER;
	end record;

    X, Y : LCD_FORM;	-- X and Y undefined.

    procedure ":=" (L : out LCD_FORM; R : in FLOAT) is
    begin
	if R = 0.0 then
	    L.NUMERATOR := 0;
	else
	    -- calculate LCD form.
	end if;
    end ":=";

begin
    X := 1.5;
    -- X.NUMERATOR is presumably 3, X.DENOMINATOR is presumably 2.

    -- Y.DENOMINATOR is undefined.
    Y := 0.0;
    -- Y.DENOMINATOR is STILL undefined.
end CIRCUMVENT;

You can object that I didn't define ":=" "correctly" and that I stupidly failed to assign to
DENOMINATOR when R is zero, but that's the whole point--I've circumvented the type checking
that is inherent in predefined assignment.

> 
>> As I understand your LCD example, you seem to see assignment overloading
>> as a vehicle for implementing type constraints that can't be expressed in
>> Ada's constraint language (such as LCD-ness).  
>> [...] 
>> Yet after all these exertions, my evenness constraint would STILL not
>> be enforced consistently, unless I was given several more language changes.
>> For example, type conversions, qualifications, attribute evaluations and
>> membership tests never propagate exceptions.  Yet what would be the effect
>> of the type conversion "EVEN(3)"?  What would be the effect of the attribute
>> evaluation "EVEN'POS(3)"?  What would be the effect of the tests
>> "2 in 0 .. 3" or "3 in 0 .. 10" ?  Should they raise CONSTRAINT_ERROR, or
>> should they round 3 to some even value?  
>
>    The problems mentioned pertain specifically to this implementation
>    only; if EVEN were implemented as a limited private type within an
>    appropriate package, all the operations mentioned would be available 
>    only through the procedures/functions provided.  Type conversions
>    would not be legally expressible in the usual notation, most attributes
>    would not be defined, etc.  Ada 83 allows us to do all of these things,
>    but not in the "conventional" notation.  

That's the whole point of why you don't need assignment overloading!!!  You can also do assignment
with limited private types, but not in the "conventional" notation.  All you need to do is provide
an "appropriate subprogram".

>
>    Since Ada does not allow implicit type conversions, the fact that we
>    cannot overload type conversion does not cause us any grief in the
>    parameter-passing process.  Nor do any of the other operations mentioned.

You seem to be missing my point.  You are trying to use an overloaded assignment operator
to implement a constraint on your LCD type that can't be expressed in Ada's constraint language.
I've argued that I would want the ability to overload the other basic operations so that
I could implement in a consistent manner the constraints that I may dream up.  You've ignored that
argument and instead still seem to be harboring the belief that assignment is "different" from
the other basic operations.  What is so unique about assignment that it alone of all the basic
operations requires special treatment?

>
>    Only assignment and destruction play vital roles in the evaluation of
>    parameters and in the creation and destruction of variables (particularly
>    with regard to block entry and exit).  It is the fact that the language
>    does not consider the impact of these mechanisms upon the ADT paradigm,
>    mechanisms which can play havoc with our abstractions, that compels us 
>    to call for changes in the language with regard to the ability to
>    define specific procedures for assignment and destruction which are 
>    taken into consideration by these mechanisms. 

We can argue about implicitly invoked destruction mechanisms some other time, since Ada provides no
such animal.  And Ada's parameter passing mechanisms are defined implicitly, beyond the reach
of the programmer, as they should be (again to enforce type checking)--they have nothing
to do with assignment at the source level.  Regarding your assignment "abstraction", all
I can offer is the suggestion that reduction to LCD form is an over-specification of what I
consider to be the normal semantics of an assignment operation, and it is better left to
another procedure whose sole purpose is to perform the reduction.  If you define your
LCD type as a limited type in a package, you can provide a NORMALIZE procedure which
will achieve the desired effect.  If you need to add new code later, your only
recourse will be to use the NORMALIZE procedure, as there is no predefined assignment operation
available to confuse you.

>    It is entirely possible that given the desire of AJPO to minimize the
>    extent of changes in the language, it will not be possible to get Ada
>    to provide complete support for the abstract data type paradigm.  Ada
>    was explicitly intended as an interim solution, covering the range
>    1983..20xx, and not as "the perfect language".  Perhaps for Ada it is
>    already too late.  But the issue needs to be thoroughly discussed,
>    because it is a major area in which Ada has "missed the boat". 

I tend to agree with your last comment.  But Ada's abstraction mechanisms will never
be improved in the next revision without a proposal that is carefully thought out and
is fully consistent with the goals of the language.  As I've tried to illustrate, none
of the proposals I've seen for assignment overloading come close to meeting these criteria.

-------------------------------------------------------------------
David Rosenblum			UUCP: {ucbvax, decvax}!ulysses!dsr
AT&T Bell Laboratories		ARPA: dsr@ulysses.att.com
600 Mountain Ave.		      dsr%ulysses@att.arpa
Murray Hill, NJ 07974-2070
(201) 582-2906
-------------------------------------------------------------------

billwolf@hubcap.clemson.edu (William Thomas Wolfe,2847,) (12/01/88)

From article <10913@ulysses.homer.nj.att.com>, by dsr@hector.UUCP (David S. Rosenblum):
> Alternatively, suppose ":=" overloadings are required to have an in out-mode 
> parameter to represent the target variable.  Then it would be impossible 
> to assign to an undefined variable, because the variable would first be 
> constraint-checked when it is passed in to the assignment procedure.

    This is a question which applies to any assignment procedure,
    whether ":=" or "ASSIGN".  The way I do it is to use in-out mode
    and implement the ADT as a pointer to a descriptor.  That way,
    I take advantage of the fact that all pointers are automatically
    initialized to null, and I use the null-pointer state as the 
    "empty" or "undefined" state of my ADT.  I'd prefer that all
    data types were automatically initialized to "undefined", so I
    could save the space and time associated with the pointer, but
    the workaround does get the job done.
 
>>    The problems mentioned pertain specifically to this implementation
>>    only; if EVEN were implemented as a limited private type within an
>>    appropriate package, all the operations mentioned would be available 
>>    only through the procedures/functions provided.  Type conversions
>>    would not be legally expressible in the usual notation, most attributes
>>    would not be defined, etc.  Ada 83 allows us to do all of these things,
>>    but not in the "conventional" notation.  
> 
> That's the whole point of why you don't need assignment overloading!!!  
> You can also do assignment with limited private types, but not in the 
> "conventional" notation.  All you need to do is provide an "appropriate 
> subprogram".

    Yes, I've been doing exactly that for a long time now.

>>    Only assignment and destruction play vital roles in the evaluation of
>>    parameters and in the creation and destruction of variables (particularly
>>    with regard to block entry and exit).  It is the fact that the language
>>    does not consider the impact of these mechanisms upon the ADT paradigm,
>>    mechanisms which can play havoc with our abstractions, that compels us 
>>    to call for changes in the language with regard to the ability to
>>    define specific procedures for assignment and destruction which are 
>>    taken into consideration by these mechanisms. 
> 
> We can argue about implicitly invoked destruction mechanisms some other time, 
> since Ada provides no such animal.  And Ada's parameter passing mechanisms 
> are defined implicitly, beyond the reach of the programmer, as they should 
> be (again to enforce type checking)--they have nothing to do with assignment 
> at the source level.  Regarding your assignment "abstraction", all I can 
> offer is the suggestion that reduction to LCD form is an over-specification 
> of what I consider to be the normal semantics of an assignment operation, 
> and it is better left to another procedure [...]

   The LCD example is not mine, and I'll leave it to the author of the
   example to make whatever counterpoints are appropriate.  But with regard
   to the point concerning enforcement of type checking, I'd like to point
   out that the example you gave was one in which you did NOT define an ADT;
   all you did was define a local record type.  I made the suggestion earlier
   that the definition of assignment, comparison, and other operators be
   restricted to the package specification in which the (limited) private 
   ADT type was defined.  The intent was to prohibit the implementation of
   an assignment operator over some limited private type declared somewhere
   else (e.g., TEXT_IO.FILE_TYPE).  Another point: the ADT-handling package
   could leave the denominator as an undefined integer if it jolly well
   felt like it, as long as it met the requirements imposed by the package
   specification.  I don't see how that would constitute any violation of
   type checking.  
   
>>    It is entirely possible that given the desire of AJPO to minimize the
>>    extent of changes in the language, it will not be possible to get Ada
>>    to provide complete support for the abstract data type paradigm.  Ada
>>    was explicitly intended as an interim solution, covering the range
>>    1983..20xx, and not as "the perfect language".  Perhaps for Ada it is
>>    already too late.  But the issue needs to be thoroughly discussed,
>>    because it is a major area in which Ada has "missed the boat". 
> 
> I tend to agree with your last comment.  But Ada's abstraction mechanisms 
> will never be improved in the next revision without a proposal that is 
> carefully thought out and is fully consistent with the goals of the 
> language.  As I've tried to illustrate, none of the proposals I've seen 
> for assignment overloading come close to meeting these criteria.

    I think providing complete support for the abstract data type paradigm
    is fully consistent with the goals of the language; as for carefully
    thought out proposals, a major purpose of this discussion is to uncover
    bugs in the details of the proposal prior to filing the 9X revision
    forms (which were made available by AJPO at Tri-Ada '88).  The points
    that have been made by you and others have done that.  Whether the 
    finalized request will be within AJPO's tolerance for change is,
    of course, still very much an open question.  But in this language
    or the next, the abstraction mechanisms available to programmers
    will be significantly stronger as a result. 


                                       Bill Wolfe

                                wtwolfe@hubcap.clemson.edu

dsr@hector.UUCP (David S. Rosenblum) (12/01/88)

In article <755@marvin.cme-durer.ARPA> leake@cme-durer.ARPA (Stephe Leake) writes:
>
>There seems to be a point of confusion concerning overloading of ":=".
>Some people assume that the (overloaded) semantics of ":=" apply to
>parameter passing. This seems to be a reasonable assumption, and I
>think the current Ada parameter passing semantics are equivalent to
>the current Ada assignment semantics.

They are not the least bit equivalent, for several reasons.  The most
obvious reason is that an implementation is free to pass composite
parameters by reference, although no implementation can "assign by
reference".  No source-level assignment operation is "invoked" to achieve
parameter passing.  Parameter passing is a language facility that is
implemented by the compiler, just as is, say, looping.

>However, other types of
>overloading (such as for "*") only apply when the overloaded symbol
>appears _explicitly_; thus an argument could be made that the
>overloaded ":=" should only be used for explicit assignments, not the
>implicit ones in parameter passing. Personally, I think parameter
>passing should be the same as assignment, but I just wanted to point
>out the confusion.

I personally believe such an interpretation of parameter passing creates
more confusion instead of less.  In essence, this interpretation adds yet
another special case to the scope and visibility rules--it allows a
subprogram call to "see past" a visible redefinition of assignment to
a predefined one that has been hidden.

>A : LCD_Fraction;
>B, C : Fraction;
>
>A := B /C ; 	-- illegal
>A := To_LCD_Fraction (B / C);	-- legal
>
>The example above brings up a suggestion; type names should be
>overloadable by functions, so we can define type conversion functions
>that look like the type conversions available to scalar types.

As I argued in the case of assignment, overloading of type conversion is
overloading of a basic operation, and such a facility would allow
one to circumvent type checking.  As you suggested above, we don't want
to turn Ada into C.


-------------------------------------------------------------------
David Rosenblum			UUCP: {ucbvax, decvax}!ulysses!dsr
AT&T Bell Laboratories		ARPA: dsr@ulysses.att.com
600 Mountain Ave.		      dsr%ulysses@att.arpa
Murray Hill, NJ 07974-2070
(201) 582-2906
-------------------------------------------------------------------

dsr@hector.UUCP (David S. Rosenblum) (12/01/88)

In article <3702@hubcap.UUCP> wtwolfe@hubcap.clemson.edu writes:
>From article <10913@ulysses.homer.nj.att.com>, by dsr@hector.UUCP (David S. Rosenblum):
>
>   The LCD example is not mine, and I'll leave it to the author of the
>   example to make whatever counterpoints are appropriate.  But with regard
>   to the point concerning enforcement of type checking, I'd like to point
>   out that the example you gave was one in which you did NOT define an ADT;
>   all you did was define a local record type.

I constructed the example that way for clarity.  The arguments are just
as valid if I make the type private in some package.  The declared
objects would still be undefined, and the assignment of 0.0 would still
leave a component undefined.

Your suggestion of implementing ADTs as access types gets around the
problems I mentioned with parameter passing, but allowing overloading of
assignment then requires a great deal of faith (misguided in my view) in
the programmer to implement his or her ADTs in such a safe manner.  I
guess you could argue that many other language features have a similarly
implicit faith in the sanity of the programmer.

>   Another point: the ADT-handling package
>   could leave the denominator as an undefined integer if it jolly well
>   felt like it, as long as it met the requirements imposed by the package
>   specification.  I don't see how that would constitute any violation of
>   type checking.  

The ADT-handling package may feel like leaving a component undefined, but
Ada doesn't feel like leaving variables undefined after an assignment.
That's why such a user-defined assignment violates Ada's strong typing.


-------------------------------------------------------------------
David Rosenblum			UUCP: {ucbvax, decvax}!ulysses!dsr
AT&T Bell Laboratories		ARPA: dsr@ulysses.att.com
600 Mountain Ave.		      dsr%ulysses@att.arpa
Murray Hill, NJ 07974-2070
(201) 582-2906
-------------------------------------------------------------------

trent@unix.SRI.COM (Ray Trent) (12/02/88)

In an article dsr@hector.UUCP (David S. Rosenblum) writes:
>domains and algebras.  However, assignment is a concept that is peculiar to
>programming languages, so in a sense you're comparing apples and oranges.

Untrue. "Let S = foo" is an idea from mathematics. But this is an aside.

>And most "sensible" definitions of assignment that I know of describe
>assignment simply in terms of placing a value in a variable or memory 

Except C's definition, of course, which is that = is an operator that
returns a value and has a domain and range.

>meaning, but you are not able to override Ada's type checking.  Thus,
>allowing overloading of assignment (or any basic operation) would seriously
>weaken Ada's strong typing features.

I'm afraid unchecked programming already does that. If the language 
designers were sooooo concerned about strong typing, why was unchecked
programming included at all? Was it perhaps because programmers know
that strong typing is a crock? I'd rather have nastiness like non-strong
typing out in the open, rather than hidden away in places where it
can't necessarily be found. (like package bodies, for example)

>Ada's constraint language (such as LCD-ness).  So why stop with assignment?
>Wouldn't you need overloading of all the basic operations?  Suppose

Of course you would. (and do)

>	type EVEN is new INTEGER;
>which I want to constrain in a way that would require all variables of
>the type to have even values.  In order to enforce this constraint
[several other basic operations overloaded]

>For example, type conversions, qualifications, attribute evaluations and
>membership tests never propagate exceptions.  Yet what would be the effect

And why not? They often should.

>perform I/O on EVEN values.  How do I get the instantiation to enforce my
>evenness constraint?  First I would need another language change that requires

You shouldn't have to in a language that purports to have strong typing.
Simply using a value outside of the constraints of the ADT should raise
a constraint error.

I don't actually propose we change the language to allow assignment
overloading. It would cause too many problems at this late date.

I'm just saying it wasn't designed right the first time. I would
argue that if assignment cannot be overloaded in any situation then
"=" should not be overloadable in any situation. In fact, I think
it's inconsistant to allow overloading of any kind if you don't allow
overloading of all kinds. After all, it's just as easy to type
"FooReal(X)" and "FooInt(X)" as it is to type just "Foo(X)", right? hurm.
And more understandable, right? Might even be more readable, neh?
Not allowing ":=" to be overloaded because it's "special" is a kludge.



-- 
"Our little lives get complicated
 It's a simple thing
 Simple as a flower
 And that's a complicated thing"                     ../ray\..

trent@unix.SRI.COM (Ray Trent) (12/02/88)

In the above article leake@cme-durer.ARPA (Stephe Leake) writes:
>does. It seems to me, therefore, that he is talking about 2 data
>types; LCD_Fractions and Fractions. This removes the need to overload

Indeed, this is the case. I explained that type Fraction was not a 
particularly good example, and why I used it anyway. I mostly object
to the internal inconsistancies of Ada...exceptions to the rules
for "special" operators, the non-object status of certain types
of procedural objects and not others, disallowing user definition
of attributes, the wierdnesses inherent in the "use" clause that
make it almost useless, the fact that Ada is supposed to 
be strongly typed, but that uninitialized variables of a type
are not required to contain a valid value in the range of that type,
and are also not required to contain an *invalid* value, etc., etc.

>Ray Trent says:
>	> The LRM explicitly says that intermediate
>	> results need not follow the constraints of the ADT.
>Where does it say this?

LRM 11.6 [6] states: Similarly, additional freedom is left to an 
implementation for the evaluation of numeric simple expressions. For
the evaluation of a predefined operation, an implementation is 
allowed to use the operation of a type that has a range wider than that of
the base type of the operands, provided that this delivers the exact result,
even if some intermediate results lie outside the range of the base type.

They only explicitly allow this for predefined operations, though it's
ambiguous enough that vendors are likely to allow it for user-defined
operations as well. (and it's kind of hard to test) I object to "special"
properties of language constructs that I am not "allowed" as a programmer
to use or duplicate in my own types, subprograms, etc., in any event.

-- 
"Our little lives get complicated
 It's a simple thing
 Simple as a flower
 And that's a complicated thing"                     ../ray\..

dsr@hector.UUCP (David S. Rosenblum) (12/02/88)

In article <24922@sri-unix.SRI.COM> trent@unix.sri.com (Ray Trent) writes:
>In an article dsr@hector.UUCP (David S. Rosenblum) writes:
>>domains and algebras.  However, assignment is a concept that is peculiar to
>>programming languages, so in a sense you're comparing apples and oranges.
>
>Untrue. "Let S = foo" is an idea from mathematics. But this is an aside.

This is NOT assignment as we know it in programming languages.  You don't
later assign new values to S, there is no "storage" of foo in S, etc.  Your
expression is simply a use of the equality relation to state an assumption
or axiom.  You are guaranteed from the substitutivity of equality that ALL
subsequent occurrences of S may be replaced by "foo".  Substitutivity of
this kind is not a property of assignment.

>>And most "sensible" definitions of assignment that I know of describe
>>assignment simply in terms of placing a value in a variable or memory 
>
>Except C's definition, of course, which is that = is an operator that
>returns a value and has a domain and range.

Yes, assignment in C is an operator.  And you've just justified further the
argument that Ada's assignment is NOT an operator, since it returns no
value.  It's seems silly to use such high-falutin' terms as "domain" and
"range" to describe assignment in C though, because due to C's weak enforcement
of type constraints, assignment can operate on arbitrary unions of domains.

>>meaning, but you are not able to override Ada's type checking.  Thus,
>>allowing overloading of assignment (or any basic operation) would seriously
>>weaken Ada's strong typing features.
>
>I'm afraid unchecked programming already does that. If the language 
>designers were sooooo concerned about strong typing, why was unchecked
>programming included at all? Was it perhaps because programmers know
>that strong typing is a crock? I'd rather have nastiness like non-strong
>typing out in the open, rather than hidden away in places where it
>can't necessarily be found. (like package bodies, for example)

Yes, unchecked programming is provided in the language, but its use is heavily
discouraged.  The semantics of unchecked programming are largely left to the
discretion of the implementor, therefore making unchecked programming
a highly non-portable feature of the language.  And note the following
(admittedly difficult-to-enforce) admonition in the LRM:  "Whenever unchecked
conversions are used, it is the programmer's responsibility to ensure
that these conversions maintain the properties that are guaranteed by the
language for objects of the target type.  Programs that violate these
properties by means of unchecked conversions are erroneous."
(Book 13, Chapter 10.2, Verse 3).

"Correct use" of Ada demands a good faith effort to construct portable
programs.  (Pardon the weasel wording.)  If you believe strong typing to
be a crock (and I'm one programmer that DOESN'T "know" that it's a
crock), then why are you lamenting the unsatisfactory character of
Ada's abstraction mechanisms?  (Maybe you never said this; I'm getting confused
as to who has argued what.)  C gives you all the "abstraction" mechanisms
you seem to desire--a wide open window to the implementation details of
your data types.

We seem to be getting away from the subject.  Somebody has suggested
allowing overloading of assignment in Ada.  I have been arguing against
this proposal from the hypothetical viewpoint of a person in charge of
updating the language.  Arguments about some of Ada's fundamental language
philosophies seem out of place in this context.

>I'm just saying it [assignment] wasn't designed right the first time. I would
>argue that if assignment cannot be overloaded in any situation then
>"=" should not be overloadable in any situation. In fact, I think
>it's inconsistant to allow overloading of any kind if you don't allow
>overloading of all kinds.

This seems like an arbitrary argument.  Overloading of operators
(in the Ada sense) seems justifiable, since the operators aren't
directly concerned with the semantics of strong typing.  You can
give kooky semantics to your favorite mathematical operator, but those
semantics may be characteristic of an abstraction.  However, overloading
of Ada's basic operations does not seem justifiable, because they are
intimately concerned with the implementation of strong typing.


-------------------------------------------------------------------
David Rosenblum			UUCP: {ucbvax, decvax}!ulysses!dsr
AT&T Bell Laboratories		ARPA: dsr@ulysses.att.com
600 Mountain Ave.		      dsr%ulysses@att.arpa
Murray Hill, NJ 07974-2070
(201) 582-2906
-------------------------------------------------------------------

dsr@hector.UUCP (David S. Rosenblum) (12/02/88)

In article <42334@linus.UUCP> eachus@mbunix.mitre.org (Robert I. Eachus) writes:
|
|     "Overloading is defined for subprograms, enumeration literals,
|operators, and single entries, and also for the operations that are
|inherent in several basic operations such as assignment, membership
|tests, allocators, the literal null, aggregates, and string literals."
|
|      Overloading, and overload resolution involving basic operations
|is a fact of Ada life.

Yes, but there is an important difference between overloading of operators,
subprograms, etc., and overloading of basic operations.  Overloaded basic
operations are ALWAYS implicitly defined--they can NEVER be user-defined.
It is the proposal to allow user-defined overloadings of basic operations
that is prompting all the "frothing".


-------------------------------------------------------------------
David Rosenblum			UUCP: {ucbvax, decvax}!ulysses!dsr
AT&T Bell Laboratories		ARPA: dsr@ulysses.att.com
600 Mountain Ave.		      dsr%ulysses@att.arpa
Murray Hill, NJ 07974-2070
(201) 582-2906
-------------------------------------------------------------------

billwolf@hubcap.clemson.edu (William Thomas Wolfe,2847,) (12/03/88)

From article <10918@ulysses.homer.nj.att.com>, by dsr@hector.UUCP (David S. Rosenblum):
> 
> The ADT-handling package may feel like leaving a component undefined, but
> Ada doesn't feel like leaving variables undefined after an assignment.
> That's why such a user-defined assignment violates Ada's strong typing.

      Really?  Consider the following:

         procedure FUN_WITH_UNDEFINED_VARIABLES is

            A : INTEGER;    -- A is undefined...
            B : INTEGER;    -- B is undefined...
         
         begin
            A := B;   -- Quick, what value does A have? 
         end FUN_WITH_UNDEFINED_VARIABLES;

      This compiles and executes under Alsys Ada.

adolph@ssc-vax.UUCP (Mark C. Adolph) (12/03/88)

In article <24856@sri-unix.SRI.COM>, trent@unix.SRI.COM (Ray Trent) writes:
> The LRM explicitly says that intermediate
> results need not follow the constraints of the ADT. 

Could you cite this in the LRM?  This could be a very important point for
we who are trying to model physical quantities (length, temperature,
density, etc.) as derived types and trying to do operations on these
types.

-- 

					-- Mark A.
					...uw-beaver!ssc-vax!adolph

billwolf@hubcap.clemson.edu (William Thomas Wolfe,2847,) (12/03/88)

From article <10917@ulysses.homer.nj.att.com>, by dsr@hector.UUCP (David S. Rosenblum):
> In article <755@marvin.cme-durer.ARPA> leake@cme-durer.ARPA (Stephe Leake) writes:
>>
>>There seems to be a point of confusion concerning overloading of ":=".
>>Some people assume that the (overloaded) semantics of ":=" apply to
>>parameter passing. This seems to be a reasonable assumption, and I
>>think the current Ada parameter passing semantics are equivalent to
>>the current Ada assignment semantics.
> 
> They are not the least bit equivalent, for several reasons.  The most
> obvious reason is that an implementation is free to pass composite
> parameters by reference, although no implementation can "assign by
> reference". 

    Sure, for "in out" mode.  But for modes "in" and "out", failure to
    use the ADT's assignment procedure causes major problems:

      "out" mode:  Programmer assigns the parameter a value, and the
                   value is copied back to the actual parameter.  
                   Unfortunately, the portions of the actual parameter
                   which were formerly accessible by pointers are now
                   uncollectable garbage.

       "in" mode:  Programmer assumes that this is a "pass by value"
                   and makes modifications.  Since ADT contains pointers,
                   modification echoes through to the actual parameter.
                   Programmer could also invoke DESTROY, blowing away
                   major portions of the actual parameter.

sommar@enea.se (Erland Sommarskog) (12/04/88)

Geoff Mendal (mendal@ANNA.STANFORD.EDU) writes:
>>The argument that pre-processors can be used to acheive this effect is
>>bull. It ignores the absolute fact that *no one* maintains, reads, or
>>is expected to understand the processed code. This is a null argument.
>
>Quite the contrary.  The whole idea of a pre-processor or translation
>tool is to remove the need to "read" the processed version.  How many
>...
>So why is it that defining a mapping from some pre-processor to Ada is
>met with such cynicism?  It simply provides an additional layer of
>abstraction,

Pre-processors are OK, if they don't appear to you as pre-processors.
For example you must make the debugger to work on the original code,
not what the pre-processor produces. Also, the pre-processor should
be genrally available, or else you will lose portability.

Many of Geoff Mendal's ideas are reasonable. Yet, I like to provide 
some examples you might long for an overloadable ":=".

When we declare our variables in blocks we often initiate them. This
makes us feel safe, we know that the variable has a defined value,
and we can deduce so by looking at the declaration part. Since ":="
is not overloadable, we cannot initiate a limited private type in a
declaration. E.g. (Text is from the Text_handler in 7.6 of the LRM.)
   A : Text := To_text("Test string");
is illegal.

My other point concerns generic parameters. I once wrote a generic
package for sorted binary trees. It's parameter list looks like this:

   Generic
      Type Data_type is limited private;
      With procedure Assign(A : in out Data_type;
                            B : in     Data_type);
      With function "<"(A, B : Data_type) return boolean is <>;
      With function ">"(A, B : Data_type) return boolean is <>;
   Package Binary_trees is

With an overloadable ":=" I could have declared the second parameter as

      With procedure ":="(A : in out Data_type;
                          B : in     Data_type) is <>;

This would save the user from declaring unnecessary Assign for types like
integer. This Assign procedure he has to write is simple, but is 100% noise 
to his code.
  (Of course I could make Data_type Private only, but in this case the user
would be unnecessarily restriced, since he would be able to instantiate
it for limited types.)

Again this was some examples since Geoff Mendall wanted some. None of
them shows that you "must" have overloadable ":=". Just some case  
there it would be nice.

Oh, someone thought that an user-defined ":=" should apply to parameter
passing as well. This is of course impossible. In that case ":=" should
define how the parameters were passed to itself. A true circular definition!
-- 
Erland Sommarskog
ENEA Data, Stockholm
sommar@enea.se
"Frequently, unexpected errors are entirely unpredictable" - Digital Equipment

sommar@enea.se (Erland Sommarskog) (12/04/88)

William Thomas Wolfe (billwolf@hubcap.clemson.edu) writes:
>     What is needed is a completion of the ADT paradigm, support for
>     which is the fundamental reason for the "limited private" feature.
>     Given that one must remember to destroy all non-predefined ADTs 
>     upon block exit, it is clear that the implications of this paradigm 
>     were not appropriately considered in the Ada 83 design. 

With other words, what you want is garbage collection. I'm quite
sure that the language designers knew about. Simula has had since
1967. Note that the language definition in no way forbids garbage
collection, but it does not require it. It says: 4.8(7) "An 
implementation may (but need not) reclaim the storage occupied by an 
object created by an allocator, once this object has become inaccessible".
In practice this means you must do all deallocating explictly if you
want to be partable.
  I think the reason why garbage collection is not required is that 
in some applications this is not very desireable from the point of 
view or performance. Typically you may not want it embedded real-time 
systems... Another thing is of course the implementation issue...

But as you point out, this is an important concept when implementing
abstract data types. I think that Ada should require garbage collection
and a pragma with which you could turn it off for critical code.
-- 
Erland Sommarskog
ENEA Data, Stockholm
sommar@enea.se
"Frequently, unexpected errors are entirely unpredictable" - Digital Equipment

billwolf@hubcap.clemson.edu (William Thomas Wolfe,2847,) (12/05/88)

From article <4123@enea.se>, by sommar@enea.se (Erland Sommarskog):
> Oh, someone thought that an user-defined ":=" should apply to parameter
> passing as well. This is of course impossible. In that case ":=" should
> define how the parameters were passed to itself. A true circular definition!

    Recall the discussion.  The rule that the "old" version of assign
    would apply throughout any function named ":=", including parameter
    passing, handles this contingency.

billwolf@hubcap.clemson.edu (William Thomas Wolfe,2847,) (12/05/88)

From article <4125@enea.se>, by sommar@enea.se (Erland Sommarskog):
> William Thomas Wolfe (billwolf@hubcap.clemson.edu) writes:
>>     What is needed is a completion of the ADT paradigm, support for
>>     which is the fundamental reason for the "limited private" feature.
>>     Given that one must remember to destroy all non-predefined ADTs 
>>     upon block exit, it is clear that the implications of this paradigm 
>>     were not appropriately considered in the Ada 83 design. 
# 
# With other words, what you want is garbage collection.  
# [...]  I think the reason why garbage collection is not required is that 
# in some applications this is not very desireable from the point of 
# view of performance. Typically you may not want it embedded real-time 
# systems... Another thing is of course the implementation issue...

   No, I do NOT want garbage collection.  Since GC cannot handle the
   problems of circular-list garbage without outrageous time expenditures,
   and for all the other reasons you mentioned above, GC is NOT desirable.
   Furthermore, GC encourages sloppy programming.  I'd be very happy to see
   GC explicitly PROHIBITED.


                                       Bill Wolfe

                               wtwolfe@hubcap.clemson.edu

dsr@hector.UUCP (David S. Rosenblum) (12/05/88)

In article <3720@hubcap.UUCP> billwolf@hubcap.clemson.edu writes:
|From article <10918@ulysses.homer.nj.att.com>, by dsr@hector.UUCP (David S. Rosenblum):
|> 
|> The ADT-handling package may feel like leaving a component undefined, but
|> Ada doesn't feel like leaving variables undefined after an assignment.
|> That's why such a user-defined assignment violates Ada's strong typing.
|
|      Really?  Consider the following:
|
|         procedure FUN_WITH_UNDEFINED_VARIABLES is
|
|            A : INTEGER;    -- A is undefined...
|            B : INTEGER;    -- B is undefined...
|         
|         begin
|            A := B;   -- Quick, what value does A have? 
|         end FUN_WITH_UNDEFINED_VARIABLES;

Easy.  A has a value that satisfies the constraints on INTEGER, which
implies that B does also.  We splitting hairs here, but I'll give it
to you--A is still undefined.

Note that you're relying on INTEGER being implicitly initialized with a valid
value; this is probably considered erroneous (I don't have an LRM
on hand).

|
|      This compiles and executes under Alsys Ada.

I don't doubt it.  Do you seriously regard this as "proof" that assignment
doesn't incorporate type checking?


-------------------------------------------------------------------
David Rosenblum			UUCP: {ucbvax, decvax}!ulysses!dsr
AT&T Bell Laboratories		ARPA: dsr@ulysses.att.com
600 Mountain Ave.		      dsr%ulysses@att.arpa
Murray Hill, NJ 07974-2070
(201) 582-2906
-------------------------------------------------------------------

dsr@hector.UUCP (David S. Rosenblum) (12/05/88)

In article <3721@hubcap.UUCP> billwolf@hubcap.clemson.edu writes:
|From article <10917@ulysses.homer.nj.att.com>, by dsr@hector.UUCP (David S. Rosenblum):
|> In article <755@marvin.cme-durer.ARPA> leake@cme-durer.ARPA (Stephe Leake) writes:
|>>
|>>There seems to be a point of confusion concerning overloading of ":=".
|>>Some people assume that the (overloaded) semantics of ":=" apply to
|>>parameter passing. This seems to be a reasonable assumption, and I
|>>think the current Ada parameter passing semantics are equivalent to
|>>the current Ada assignment semantics.
|> 
|> They are not the least bit equivalent, for several reasons.  The most
|> obvious reason is that an implementation is free to pass composite
|> parameters by reference, although no implementation can "assign by
|> reference". 
|
|    Sure, for "in out" mode.  But for modes "in" and "out", failure to
|    use the ADT's assignment procedure causes major problems:
|
|      "out" mode:  Programmer assigns the parameter a value, and the
|                   value is copied back to the actual parameter.  
|                   Unfortunately, the portions of the actual parameter
|                   which were formerly accessible by pointers are now
|                   uncollectable garbage.
|
|       "in" mode:  Programmer assumes that this is a "pass by value"
|                   and makes modifications.  Since ADT contains pointers,
|                   modification echoes through to the actual parameter.
|                   Programmer could also invoke DESTROY, blowing away
|                   major portions of the actual parameter.

I'm terribly confused.  Are we still talking about Ada?  With this talk
about parameter passing invoking source-level assignment, I'm not so sure.
I'm not sure what your "out" mode comments refer to, but maybe you're
confusing what access values (pointers) can point to in Ada, or maybe you're
confusing how access values are passed as parameters in Ada--access values are
ALWAYS copied IN to their respective places in formal parameters, EVEN for
mode "out" formals.  Your "in" mode comments are also vague.  First of all,
"good" Ada programmers assume nothing about parameter passing behavior.
Second, an access value can be passed as a parameter, in which case the
language is concerned only with the access value during parameter passing,
NOT with the object designated by the access value.


-------------------------------------------------------------------
David Rosenblum			UUCP: {ucbvax, decvax}!ulysses!dsr
AT&T Bell Laboratories		ARPA: dsr@ulysses.att.com
600 Mountain Ave.		      dsr%ulysses@att.arpa
Murray Hill, NJ 07974-2070
(201) 582-2906
-------------------------------------------------------------------

billwolf@hubcap.clemson.edu (William Thomas Wolfe,2847,) (12/05/88)

From article <10960@ulysses.homer.nj.att.com>, by dsr@hector.UUCP (David S. Rosenblum):
> |> They are not the least bit equivalent, for several reasons.  The most
> |> obvious reason is that an implementation is free to pass composite
> |> parameters by reference, although no implementation can "assign by
> |> reference". 
> |
> |    Sure, for "in out" mode.  But for modes "in" and "out", failure to
> |    use the ADT's assignment procedure causes major problems:
> |
> |      "out" mode:  Programmer assigns the parameter a value, and the
> |                   value is copied back to the actual parameter.  
> |                   Unfortunately, the portions of the actual parameter
> |                   which were formerly accessible by pointers are now
> |                   uncollectable garbage.
> |
> |       "in" mode:  Programmer assumes that this is a "pass by value"
> |                   and makes modifications.  Since ADT contains pointers,
> |                   modification echoes through to the actual parameter.
> |                   Programmer could also invoke DESTROY, blowing away
> |                   major portions of the actual parameter.
> 
> I'm terribly confused.  Are we still talking about Ada?  With this talk
> about parameter passing invoking source-level assignment, I'm not so sure.
> I'm not sure what your "out" mode comments refer to, but maybe you're
> confusing what access values (pointers) can point to in Ada, or maybe you're
> confusing how access values are passed as parameters in Ada--access values are
> ALWAYS copied IN to their respective places in formal parameters, EVEN for
> mode "out" formals.  

     OK.  Envision an ADT which just happens to be implemented as a pointer
     to a descriptor, which probably has more pointers to the "value".  Now
     our ADT user wishes to transfer an ADT "out".  The ADT user has no idea
     how the ADT is implemented.  Now when the procedure was invoked, let's 
     assume that the ADT supplied as an actual parameter had some value;
     hence, the pointer which represents the "ADT type" is not null, and 
     in fact constitutes the only known method for accessing a substantial
     amount of utilized memory.  Our ADT user sends the value "out", and
     Ada implements this by copying the value of a single pointer.  The old
     pointer value associated with the actual parameter is lost forever,
     and all the stuff it pointed to is now garbage.
 
> Your "in" mode comments are also vague.  First of all,
> "good" Ada programmers assume nothing about parameter passing behavior.

     Anything which is passed as an "in" parameter should be treatable as
     something which was passed in by value.  Anything which is passed as
     an "in out" parameter should be treatable as something which was passed
     by reference.  Anything which is passed as an "out" parameter should
     be treated as the target of a pass by value in the "outward" direction.

     If nothing can be assumed about parameter passing behavior, then 
     parameter passing is just too vaguely defined, and needs to be clarified.

> Second, an access value can be passed as a parameter, in which case the
> language is concerned only with the access value during parameter passing,
> NOT with the object designated by the access value.

     When what is being passed is known to the user as an access value,
     this is precisely what the programmer expects.  When what is being
     passed is known to the user as an ADT, this sort of behavior is
     totally counterintuitive.  It violates the abstraction.


                                          Bill Wolfe

                                  wtwolfe@hubcap.clemson.edu

billwolf@hubcap.clemson.edu (William Thomas Wolfe,2847,) (12/05/88)

From article <10959@ulysses.homer.nj.att.com>, by dsr@hector.UUCP (David S. Rosenblum):
> In article <3720@hubcap.UUCP> billwolf@hubcap.clemson.edu writes:
> |From article <10918@ulysses.homer.nj.att.com>, by dsr@hector.UUCP (David S. Rosenblum):
> |> 
> |> The ADT-handling package may feel like leaving a component undefined, but
> |> Ada doesn't feel like leaving variables undefined after an assignment.
# |> That's why such a user-defined assignment violates Ada's strong typing.
# |
# |         [FUN_WITH_UNDEFINED_VARIABLES]
# 
# Easy.  A has a value that satisfies the constraints on INTEGER, which
% implies that B does also.  We splitting hairs here, but I'll give it
% to you--A is still undefined.
% 
% Note that you're relying on INTEGER being implicitly initialized with a valid
% value; this is probably considered erroneous (I don't have an LRM
@ on hand).
@ 
@ |
@ |      This compiles and executes under Alsys Ada.
@ 
@ I don't doubt it.  Do you seriously regard this as "proof" that assignment
@ doesn't incorporate type checking?

     Well, you are quoted above as saying (correct me if I'm wrong...) that
     Ada somehow required that a variable be defined after assignment, and
     that a user-defined assignment such as the one you described could leave
     a component undefined and therefore violate strong typing.  

     As far as I'm concerned, if we implemented your example as a user-defined
     ADT, the only problem that could occur would be failure of the implementor
     to properly respond to a request for the value of the denominator.  If the
     implementor handles the request properly, it wouldn't matter if there were
     undefined components; the user can never access them anyway, and in general
     will never know the difference.  In other words, the implementor may well
     view the state in which the denominator is undefined as a valid state of
     the ADT.  I see nothing wrong with that. 
  

ok@quintus.uucp (Richard A. O'Keefe) (12/05/88)

In article <3733@hubcap.UUCP> wtwolfe@hubcap.clemson.edu writes:
>   No, I do NOT want garbage collection.  Since GC cannot handle the
>   problems of circular-list garbage without outrageous time expenditures,

Are you up-to-date on this?  There has been a lot of work done on GC in
the last 5 years.

>   Furthermore, GC encourages sloppy programming.

How so?  Have you some data on this?  I very much prefer languages with
automatic storage management (Lisp, Pop, Prolog, Simula 67, SmallTalk, ...)
because there is a large class of mistakes you simply can't make (e.g.
referring to nonexistent storage) and the only real sacrifice in
expressiveness is that it is harder to write programs which crash when a
fixed-size buffer overflows (:-).  It would be a service if you could
elaborate on this:  if I knew what sort of sloppiness was encouraged I
could more effectively try to avoid it.

sommar@enea.se (Erland Sommarskog) (12/05/88)

William Thomas Wolfe (billwolf@hubcap.clemson.edu) writes:
>I said:
>> Oh, someone thought that an user-defined ":=" should apply to parameter
>> passing as well. This is of course impossible. In that case ":=" should
>> define how the parameters were passed to itself. A true circular definition!
>
>    Recall the discussion.  The rule that the "old" version of assign
>    would apply throughout any function named ":=", including parameter
>    passing, handles this contingency.

So there should be one rule for passing parameters to ":=" and another
to all other subprograms? That, if nothing else, shows that there is
something wrong with the idea. You expect all subprograms to behave
the same. What if ":=" is renamed? Should the rules change? And if
":=" calls another subprogram, what rules should be applied in this
case?
  Unless you don't want to fill the language definition with special
cases, and I don't, the idea to have a user-defined ":=" to implicitly
define parameter passing seems dead. If you really want it that way
you should think of some new syntactic device for defining assignment
of a limited type.
-- 
Erland Sommarskog
ENEA Data, Stockholm
sommar@enea.se
"Frequently, unexpected errors are entirely unpredictable" - Digital Equipment

billwolf@hubcap.clemson.edu (William Thomas Wolfe,2847,) (12/05/88)

From article <808@quintus.UUCP>, by ok@quintus.uucp (Richard A. O'Keefe):
> In article <3733@hubcap.UUCP> wtwolfe@hubcap.clemson.edu writes:
>>   No, I do NOT want garbage collection.  Since GC cannot handle the
>>   problems of circular-list garbage without outrageous time expenditures,
> 
> Are you up-to-date on this?  There has been a lot of work done on GC in
> the last 5 years.

    No, I'm not, but I'll bet GC still can't be done in O(n) time 
    with regard to the number of items of garbage, with constants
    no greater than the overhead of a deallocation...

>>   Furthermore, GC encourages sloppy programming.
> 
> It would be a service if you could elaborate on this [...]

    The programmer is Johnny-on-the-spot.  He/she KNOWS whether or not
    a given object is no longer needed.  All that is necessary is an 
    indication of this fact via the command DESTROY (Unneeded_Object).

    If the programmer is sloppy and leaves garbage lying all over the place,
    the "maid" must do all the hard work of deciding what to keep 
    and what to throw out.  Furthermore, this price must be paid
    repeatedly at execution time.  This is a heavy price to pay for
    not simply designing it right the first time.  

dsr@hector.UUCP (David S. Rosenblum) (12/05/88)

In article <3736@hubcap.UUCP> billwolf@hubcap.clemson.edu writes:
|
|     Well, you are quoted above as saying (correct me if I'm wrong...) that
|     Ada somehow required that a variable be defined after assignment, and
|     that a user-defined assignment such as the one you described could leave
|     a component undefined and therefore violate strong typing.  

Yes, that was an exaggeration that I am more than happy to retract.  Replace
that statement with "Ada doesn't feel like allowing a variable to have
a value outside of its type after an assignment to the variable.  Your
example certainly didn't demonstrate any weaknesses in Ada's type checking
semantics, which has been the real crux of the whole discussion.

|
|     As far as I'm concerned, if we implemented your example as a user-defined
|     ADT, the only problem that could occur would be failure of the implementor
|     to properly respond to a request for the value of the denominator.  If the
|     implementor handles the request properly, it wouldn't matter if there were
|     undefined components; the user can never access them anyway, and in general
|     will never know the difference.  In other words, the implementor may well
|     view the state in which the denominator is undefined as a valid state of
|     the ADT.  I see nothing wrong with that. 
|  

For the umpteenth time, the point of my example was to show that user-definable
assignment is a means of circumventing type checking.  I was not concerned
with the mindset of the implementor or the user of the abstraction--we can
talk all day about what an ideal safe programmer would do.  If you
find such a weak semantics for := acceptable, then you are certainly entitled
to your opinion.  I'm just suggesting that you'll probably never see
such a semantics in Ada.

I'm afraid I've exhausted whatever supply of insight I may have been able
to offer on assignment overloading.  I feel I've spent more space quoting
the reference manual on language fundamentals than I have on arguing any
substantive points.  In short, Ada is not a language to consider using if you
desire (1) condoned mechanisms for circumventing type checking, (2) parameter
passing mechanisms defined in terms of assignment, as assignment is defined
at the source level.


-------------------------------------------------------------------
David Rosenblum			UUCP: {ucbvax, decvax}!ulysses!dsr
AT&T Bell Laboratories		ARPA: dsr@ulysses.att.com
600 Mountain Ave.		      dsr%ulysses@att.arpa
Murray Hill, NJ 07974-2070
(201) 582-2906
-------------------------------------------------------------------

dsr@hector.UUCP (David S. Rosenblum) (12/05/88)

In article <3734@hubcap.UUCP> wtwolfe@hubcap.clemson.edu writes:
|     OK.  Envision an ADT which just happens to be implemented as a pointer
|     to a descriptor, which probably has more pointers to the "value".  Now
|     our ADT user wishes to transfer an ADT "out".  The ADT user has no idea
|     how the ADT is implemented.  Now when the procedure was invoked, let's 
|     assume that the ADT supplied as an actual parameter had some value;
|     hence, the pointer which represents the "ADT type" is not null, and 
|     in fact constitutes the only known method for accessing a substantial
|     amount of utilized memory.  Our ADT user sends the value "out", and
|     Ada implements this by copying the value of a single pointer.  The old
|     pointer value associated with the actual parameter is lost forever,
|     and all the stuff it pointed to is now garbage.

The language already has a very nice feature to handle such situations.
It's called the "limited type".  By implementing your ADT as a limited
type, you deny the user of the ADT the ability to assign to variables and
in-out and out mode parameters of the type.  The only way to assign
to such a parameter or variable is to pass it to a subprogram that is
part of the implementation of the ADT.  That subprogram can then reclaim
the storage refernced by the access value if it decides to give the parameter
a new value.  Geoff Mendal did a nice paper on the encapsulation of storage
reclamation mechanisms inside user-defined ADTs in a paper he gave at a
recent SIGAda (you can get a copy from him).

|     Anything which is passed as an "in" parameter should be treatable as
|     something which was passed in by value.  Anything which is passed as
|     an "in out" parameter should be treatable as something which was passed
|     by reference.  Anything which is passed as an "out" parameter should
|     be treated as the target of a pass by value in the "outward" direction.
|
|     If nothing can be assumed about parameter passing behavior, then 
|     parameter passing is just too vaguely defined, and needs to be clarified.

You'll have to take this up with the Ada Language Maintenance Committee.
The language designers, and many Ada programmers, believe it to be as
clearly defined as it needs to be so that portable programs may be written.

|     When what is being passed is known to the user as an access value,
|     this is precisely what the programmer expects.  When what is being
|     passed is known to the user as an ADT, this sort of behavior is
|     totally counterintuitive.  It violates the abstraction.

Right.  So as I said above, define the ADT as limited private, so that
you (as the implementor of the ADT) can have complete control over the
use of the ADT.  That way the user of the ADT won't be able to violate the
abstraction.

Back to work.


-------------------------------------------------------------------
David Rosenblum			UUCP: {ucbvax, decvax}!ulysses!dsr
AT&T Bell Laboratories		ARPA: dsr@ulysses.att.com
600 Mountain Ave.		      dsr%ulysses@att.arpa
Murray Hill, NJ 07974-2070
(201) 582-2906
-------------------------------------------------------------------

billwolf@hubcap.clemson.edu (William Thomas Wolfe,2847,) (12/06/88)

From article <10963@ulysses.homer.nj.att.com>, by dsr@hector.UUCP (David S. Rosenblum):
@ By implementing your ADT as a limited
@ type, you deny the user of the ADT the ability to assign to variables and
@ in-out and out mode parameters of the type.  The only way to assign
# to such a parameter or variable is to pass it to a subprogram that is
# part of the implementation of the ADT.  That subprogram can then reclaim
# the storage refernced by the access value if it decides to give the parameter
# a new value.  

   Sure, except for the circumvention of the provided ASSIGN routine which
   occurs every time the ADT is passed as an "in" or "out" parameter.

   There is no way to deny the user this particular method of circumventing
   your assignment routine, or even to have the compiler warn the user
   that a limited private ADT cannot be relied upon as a value parameter.
   
> |     When what is being passed is known to the user as an access value,
> |     this is precisely what the programmer expects.  When what is being
$ |     passed is known to the user as an ADT, this sort of behavior is
$ |     totally counterintuitive.  It violates the abstraction.
$ 
$ Right.  So as I said above, define the ADT as limited private, so that
% you (as the implementor of the ADT) can have complete control over the
% use of the ADT.  That way the user of the ADT won't be able to violate the
% abstraction.

    Except by way of the parameter passing mechanism.

ok@quintus.uucp (Richard A. O'Keefe) (12/06/88)

In article <3740@hubcap.UUCP> billwolf@hubcap.clemson.edu writes:
>From article <808@quintus.UUCP>, by ok@quintus.uucp (Richard A. O'Keefe):
>> It would be a service if you could elaborate on this [...]

>    The programmer is Johnny-on-the-spot.  He/she KNOWS whether or not
>    a given object is no longer needed.  All that is necessary is an 
>    indication of this fact via the command DESTROY (Unneeded_Object).

This simply isn't true.  If I write a library routine which is passed
some linked data structure, I may be able to tell whether my traversal
of it has finished, but that leaves me with no idea whatsoever of
whether I was given the last reference to it (in which case now is the
time to delete it) or not.  Or suppose some "object" has references to
some other objects, and I DESTROY(..) the first object.  Should it
DESTROY(..) the other objects?  In that case it may destroy things which
other objects still refer to!  But if it doesn't, and it held the only
references to those objects, their space will not be reclaimed.  Is
Johnny on the spot?  NO!  The programmer who coded the implementation
of that object has _no_idea_ how other programmers will use it.  There
has been quite a lot of discussion about this in comp.lang.c++ .

     Let me apply billwolf@hubcap's general principle to another case:

	The programmer is Johnny-on-the-spot.  He/she KNOWS whether a
	given object is an integer or a float.  All that is necessary
	is an indication of this fact via the use of + for integers
	and #+ for floats.  We don't need any of this strong typing
	nonsense!

Automatic storage management, like automatic operator selection, means
that the programmer misses an opportunity to make a mistake.  There 
_are_ garbage collection methods around where the overhead of
garbage collection is a constant times the cost of allocation.

ADA was explicitly designed not to require garbage collection.
Since it was meant to be useful for real-time work, that was appropriate.

billwolf@hubcap.clemson.edu (William Thomas Wolfe,2847,) (12/07/88)

From article <812@quintus.UUCP>, by ok@quintus.uucp (Richard A. O'Keefe):
> In article <3740@hubcap.UUCP> billwolf@hubcap.clemson.edu writes:
>>From article <808@quintus.UUCP>, by ok@quintus.uucp (Richard A. O'Keefe):
>>> It would be a service if you could elaborate on this [...]
> 
>>    The programmer is Johnny-on-the-spot.  He/she KNOWS whether or not
$>    a given object is no longer needed.  All that is necessary is an 
$>    indication of this fact via the command DESTROY (Unneeded_Object).
$ 
$ This simply isn't true.  If I write a library routine which is passed
$ some linked data structure, I may be able to tell whether my traversal
$ of it has finished, but that leaves me with no idea whatsoever of
$ whether I was given the last reference to it (in which case now is the
$ time to delete it) or not.  

      Whether or not the library routine should DESTROY its parameter
      depends on how the library routine is defined.  How about a
      more specific example?

# Or suppose some "object" has references to
# some other objects, and I DESTROY(..) the first object.  Should it
# DESTROY(..) the other objects?  In that case it may destroy things which
# other objects still refer to!  But if it doesn't, and it held the only
# references to those objects, their space will not be reclaimed.  

     Again, we need specifics here.  I hope you aren't talking about
     structural sharing here, which should NEVER be seriously contemplated.
 
% Is Johnny on the spot?  NO!  The programmer who coded the implementation
% of that object has _no_idea_ how other programmers will use it.  

     The implementor has no idea what *applications* the object will be
     used in, but has almost total control (the exceptions occur during
     parameter passing and block exit) over the operations of the ADT.

& There _are_ garbage collection methods around where the overhead of
& garbage collection is a constant times the cost of allocation.
 
     I specified O(n) with respect to the cost of each DEallocation,
     with the cost no higher than the overhead of doing the deallocation.
     This implies that the garbage collection routine has the ability to
     magically know the size and location of each and every piece of garbage,
     and has no need to examine any non-garbage. 

leake@cme.nbs.gov (Stephe Leake) (12/08/88)

In article <10917@ulysses.homer.nj.att.com> dsr@hector.UUCP (David S. Rosenblum) writes:

   As I argued in the case of assignment, overloading of type conversion is
   overloading of a basic operation, and such a facility would allow
   one to circumvent type checking.  As you suggested above, we don't want
   to turn Ada into C.

Type checking _cannot_ be circumvented by overloading of type
conversion! Consider:

type DEGREES is digits 6 range 0.0 .. 360.0;
type RADIANS is digits 6;

function DEGREES (Item : in RADIANS) return DEGREES
is
begin
	return Item * 180.0 / PI;	-- range constraint is checked!
end;

Since the underlying Ada type checking is done on the function result,
no circumvention of type checking is possible.

On the other hand, the fact that type conversion is a basic operation
is significant; allowing overloading would mean changing it to a
normal operation (is "normal" the complement of "basic"?), with
corresponding changes in visibility. As I see it, there are two gains;
first, I get to name my type conversion functions the same as the
implicit type conversions (ie DEGREES instead of TO_DEGREES).  Second,
I get to hide the implicit conversion, which is appropriate in the
case of DEGREES and RADIANS. However, Ada provides limited types for
hiding purposes, so this second point is not really valid (although a
limited type seems like a lot of effort for such a simple thing,
remember that Ada was intended for maintainers, not programmers :-).
So it probably isn't worth persuing. (Sigh - one of these days I'll
have a really good idea :-).

Stephe Leake 	(301) 975-3431 		leake@cme.nbs.gov
National Institute of Standards and Technology
(formerly National Bureau of Standards)
Rm. B-124, Bldg. 220
Gaithersburg, MD  20899

leake@cme.nbs.gov (Stephe Leake) (12/08/88)

In article <24934@sri-unix.SRI.COM> trent@unix.SRI.COM (Ray Trent) writes:

   ... I mostly object
   to the internal inconsistancies of Ada...exceptions to the rules
   for "special" operators, the non-object status of certain types
   of procedural objects and not others, disallowing user definition
   of attributes, the wierdnesses inherent in the "use" clause that
   make it almost useless, the fact that Ada is supposed to 
   be strongly typed, but that uninitialized variables of a type
   are not required to contain a valid value in the range of that type,
   and are also not required to contain an *invalid* value, etc., etc.

That is the purpose of this discussion; to find the "inconsistencies"
in Ada, and propose solutions. So far, I don't think any of the
proposed solutions are better than what we already have. Certainly Ada
is more consistent than C! Also remember that Ada is very powerful,
and is _not_ simple; things that may appear inconsistent are actually
logical consequences of this power.

   LRM 11.6 [6] states: Similarly, additional freedom is left to an 
   implementation for the evaluation of numeric simple expressions. For
   the evaluation of a predefined operation, an implementation is 
   allowed to use the operation of a type that has a range wider than that of
   the base type of the operands, provided that this delivers the exact result,
   even if some intermediate results lie outside the range of the base type.

   They only explicitly allow this for predefined operations, though it's
   ambiguous enough that vendors are likely to allow it for user-defined
   operations as well. (and it's kind of hard to test) I object to "special"
   properties of language constructs that I am not "allowed" as a programmer
   to use or duplicate in my own types, subprograms, etc., in any event.

I don't see any ambiguity: "numeric simple expressions" and
"predefined operations" are very well defined, and user ADT's do not
fit the definitions!

On the other hand, I agree that this is a "special" exception to the
general rule. It has a very good justification, however; any hardware
must have only a few types (32 and 16 integers, 64 and 32 bit float,
for example) to be efficient (although I sometimes daydream about the
connection machine, where I could implement a 25 bit float if I wanted
to :-). Ada was intended to be a practical language, as well as an
abstract one.  Thus compromises are sometimes necessary.

Stephe Leake 	(301) 975-3431 		leake@cme.nbs.gov
National Institute of Standards and Technology
(formerly National Bureau of Standards)
Rm. B-124, Bldg. 220
Gaithersburg, MD  20899

leake@cme.nbs.gov (Stephe Leake) (12/08/88)

In article <3734@hubcap.UUCP> billwolf@hubcap.clemson.edu (William Thomas Wolfe,2847,) writes:

	OK.  Envision an ADT which just happens to be implemented as a pointer
	to a descriptor, which probably has more pointers to the "value".  Now
	our ADT user wishes to transfer an ADT "out".  The ADT user has no idea
	how the ADT is implemented.  Now when the procedure was invoked, let's 
	assume that the ADT supplied as an actual parameter had some value;
	hence, the pointer which represents the "ADT type" is not null, and 
	in fact constitutes the only known method for accessing a substantial
	amount of utilized memory.  Our ADT user sends the value "out", and
	Ada implements this by copying the value of a single pointer.  The old
	pointer value associated with the actual parameter is lost forever,
	and all the stuff it pointed to is now garbage.

This is precisely the reason for limited private types; only functions
provide by the ADT package may have out parameters of the limited
type, so the ADT programmer can ensure correct behaviour, including
garbage collection and copying. This is a much simpler solution than
forcing parameter passing to use the ADT assignment operator.

	Anything which is passed as an "in" parameter should be treatable as
	something which was passed in by value.  Anything which is passed as
	an "in out" parameter should be treatable as something which was passed
	by reference.  Anything which is passed as an "out" parameter should
	be treated as the target of a pass by value in the "outward" direction.


	If nothing can be assumed about parameter passing behavior, then 
	parameter passing is just too vaguely defined, and needs to be clarified.

The program should be independent of the choice of parameter passing
implementation. (LRM 6.2 (7) says so).  What can be assumed about
parameter passing behaviour is precisely what the LRM says (mostly in
6.2,3,4); no more, and no less. If you want different semantics, use a
limited type (and document the semantics in the package spec).

   > Second, an access value can be passed as a parameter, in which case the
   > language is concerned only with the access value during parameter passing,
   > NOT with the object designated by the access value.

	When what is being passed is known to the user as an access value,
	this is precisely what the programmer expects.  When what is being
	passed is known to the user as an ADT, this sort of behavior is
	totally counterintuitive.  It violates the abstraction.

Once again, this is why limited types are available. It is up to the
ADT implementor to insure that abstraction violations are not
possible. This is not always easy.

Stephe Leake 	(301) 975-3431 		leake@cme.nbs.gov
National Institute of Standards and Technology
(formerly National Bureau of Standards)
Rm. B-124, Bldg. 220
Gaithersburg, MD  20899

leake@cme.nbs.gov (Stephe Leake) (12/08/88)

In article <10962@ulysses.homer.nj.att.com> dsr@hector.UUCP (David S. Rosenblum) writes:

   ... I feel I've spent more space quoting
   the reference manual on language fundamentals than I have on arguing any
   substantive points.  ...

I, for one, appreciate the time and the pointers. The LRM is complex
enough that it takes discussions like this to fully educate the user.

Thanks!

Stephe Leake 	(301) 975-3431 		leake@cme.nbs.gov
National Institute of Standards and Technology
(formerly National Bureau of Standards)
Rm. B-124, Bldg. 220
Gaithersburg, MD  20899

leake@cme.nbs.gov (Stephe Leake) (12/08/88)

In article <3757@hubcap.UUCP> billwolf@hubcap.clemson.edu (William Thomas Wolfe,2847,) writes:

      Sure, except for the circumvention of the provided ASSIGN routine which
      occurs every time the ADT is passed as an "in" or "out" parameter.

A limited type CANNOT be passed as an out parameter!!!!
A limited type CANNOT be passed as an out parameter!!!!
(in a user routine) LRM 7.4.4 (4). Have you got the message now? And
for an in parameter, the only access to the object is thru the ADT
functions, which work as desired.

Do you have an explicit example where a limited type violates the
abstraction in the way you are discussing? If so, maybe you have found
a compiler bug.

Stephe Leake 	(301) 975-3431 		leake@cme.nbs.gov
National Institute of Standards and Technology
(formerly National Bureau of Standards)
Rm. B-124, Bldg. 220
Gaithersburg, MD  20899

dsr@hector.UUCP (David S. Rosenblum) (12/09/88)

In article <772@marvin.cme.nbs.gov> leake@cme.nbs.gov (Stephe Leake) writes:
>In article <10917@ulysses.homer.nj.att.com> dsr@hector.UUCP (David S. Rosenblum) writes:
>
>   As I argued in the case of assignment, overloading of type conversion is
>   overloading of a basic operation, and such a facility would allow
>   one to circumvent type checking.  As you suggested above, we don't want
>   to turn Ada into C.
>
>Type checking _cannot_ be circumvented by overloading of type
>conversion! Consider:
>
>type DEGREES is digits 6 range 0.0 .. 360.0;
>type RADIANS is digits 6;
>
>function DEGREES (Item : in RADIANS) return DEGREES
>is
>begin
>	return Item * 180.0 / PI;	-- range constraint is checked!
>end;
>
>Since the underlying Ada type checking is done on the function result,
>no circumvention of type checking is possible.

My comment did NOT have to do with checking the constraints of the
target type.  I hope anybody with a minimal familiarity with Ada knows
that type constraints are checked on function return values.  My point
was that overloading of type conversion will allow you to "covert" pointers
to integers, arrays to files, tasks to widgets, etc.  This is called "casting"
in C, better known as "anything goes".  To me such conversions are a
circumvention of Ada's strong typing model.

I'll try this just one more time.  By allowing the overloading of
basic operations, you may gain some flexibility in defining your
abstractions, but this gain will be achieved only at the (to me, unjustifiable)
cost of severely weakening Ada's strong typing strictures.  Any
proposal to increase flexibility in defining abstractions in Ada
MUST ("read my lips"--MUST) conform to the fundamental language philosophies
of Ada.

I would suggest two promising areas for making such improvements
in an "Ada-like" way.  (1) Allow user-specifiable initialization and
destruction routines for types.  Ray Trent and Bill Wolfe have been arguing
for a parameter passing semantics based on assignment.  Stroustrup, in his
C++ book, argues that you need initialization mechanisms, not assignment, for
parameter passing, and that initialization and assignment are
fundamentally different operations.  Initialization routines would
be appropriate for initializing parameters that are passed by copy, which
(at least for scalars) is the mandated parameter passing mechanism.
(2) Define a mechanism for allowing richer user-specified constraints
on types, e.g. "all objects of type EVEN must have even values", or "for
all objects of record type FOO, component BAR must always be greater than
component BAZ".  Then let Ada's inherent type checking mechanisms check
these constraints automatically.

In short, Ada has a very nice type checking framework.  We should try to
come up with proposals that improve that framework in ways that increases
its flexibility without decreasing its robustness; don't just propose tacking
on new language rules and band-aids that will cheaply rid you of your
frustrations with the current framework.


-------------------------------------------------------------------
David Rosenblum			UUCP: {ucbvax, decvax}!ulysses!dsr
AT&T Bell Laboratories		ARPA: dsr@ulysses.att.com
600 Mountain Ave.		      dsr%ulysses@att.arpa
Murray Hill, NJ 07974-2070
(201) 582-2906
-------------------------------------------------------------------