Version 1.5 of ais/ai-00323.txt

Unformatted version of ais/ai-00323.txt version 1.5
Other versions for file ais/ai-00323.txt

!standard 6.01 (18)          03-01-22 AI95-00323/01
!standard 6.06 (03)
!standard 9.5.1 (02)
!class amendment 03-01-23
!status No Action (7-1-0) 03-02-08
!status work item 03-01-23
!status received 03-01-23
!priority High
!difficulty Easy
!subject Allow in out parameters for functions.
!summary
Parameters of all modes are allowed in functions.
!problem
The restriction on parameter modes for functions is an embarassing wart on the language. Ada functions can have arbitrary side-effects, but are not allowed to announce that in their specifications.
When constructing reusable object-oriented abstractions, it is important to allow future extensions to have the maximum flexibility. This means that parameters of object-oriented types should generally be declared as in out, so that future extensions can modify the parameter's contents if necessary.
However, this prevents using functions for most operations. If there is any conceptual chance of an extension needing to modify its argument, then a function cannot be used. Even for operations that are conceptually pure, caching of results may require modification of extensions.
If an unconstrained type (such as String) needs to be returned, there is no real alternative to using a function.
When this problem occurs, only three solutions are possible:
-- Abandon functions; -- Use the functions, leaving the onus of solving the problem on
extensions.
-- Use an access parameter.
For the second solution, the author of an extension would have to allocate (probably from the heap) a separate, writable object, and include an access to it in the extension. This means the author now has to handle memory management (with all of its possibilities for errors, like storage leaks and double freeing) which they previously did not have to do.
Access parameters are a very flawed solution as well. Access parameters require altering the calling syntax of calls to the function (to include an approriate 'Access, or to remove a dereference). They also require altering the declaration of the object to be passed (to include "aliased"). Finally, access parameters are more expensive at run-time than regular parameter passing, as they are required to include a run-tine indication of their accessibility level. The run-time check can even be dangerous, as the check may fail only with arguments declared in nested scopes, and that may not happen in unit testing.
for all of these reasons, the restriction on parameter modes for functions has been removed.
!proposal
(See summary.)
!wording
The last sentence of 6.1(18) is deleted.
Add "of mode in" to 6.6(3): The subprogram_specification of a unary or binary operator shall have one or two parameters of mode in, respectively. A generic function instantiation whose designator is an operator_symbol is only allowed if the specification of the generic function has the corresponding number of parameters of mode in.
Replace 9.5.1(2) by: Within the body of a protected function (or a function declared immediately within a protected_body), all of whose parameters have mode in, the current instance of the enclosing protected unit is defined to be a constant [(that is, its subcomponents may be read but not updated)]. Within the body of a protected subprogram other than a function with parameters of mode in (or a such a subprogram declared immediately within a protected_body), and within an entry_body, the current instance is defined to be a variable [(updating is permitted)].
!discussion
This is primarily a political issue, rather than a technical one. Access parameters act very similarly to in out parameters on functions, they just provide a much less convenient syntax and are less efficient.
Two additional issues are addressed by this change.
Operator symbols are conceptually functions with one or two in parameters. We do not want infix expressions to have side effects on their arguments. Thus, we add a rule restricting operator symbols to parameters of mode in.
The unfortunate tying of read-only vs. read-write access to a protected object to the use of a function or procedure also needs to be changed. We've used the minimal fix here, which is to say that functions with in out or out parameters have read-write access to the protected object. A better solution would be to add a modifier similar to that used for overriding to determine whether a subprogram has read-only versus read-write access. However, that was rejected as being too heavy of a solution, particularly as Ada compilers rarely take advantage of read-only protected object access.
!example
In the Claw Builder, we have a function that can return an access to the actual Claw object for simulation. This function is usually used to directly call a dispatching Claw operation:
Claw.Move (Get_Claw_Object (My_Button).all, <new location>);
Because the access returned is never used for more than a single subprogram, we implemented the function with Unchecked_Access:
function Get_Claw_Object (Window : in Button_Type) return Claw.Any_Window_Access_Type is begin return Window.My_Button'Unchecked_Access; end Get_Claw_Object;
This code is illegal, of course, as Window.My_Button is a constant, and Any_Window_Access_Type is an access-to-variable. Since Claw.Move takes an in out parameter, changing Any_Window_Access_Type is out of the question.
Changing this to the "obvious" implementation:
function Get_Claw_Object (Window : access Button_Type) return Claw.Any_Window_Access_Type is begin return Window.My_Button'Unchecked_Access; end Get_Claw_Object;
means that calling objects have to be declared as aliased, and an additional 'Access used. Moreover, there is the run-time overhead of passing an accessibility level, even though it will never be used in this case.
What we really want, of course, is to write:
function Get_Claw_Object (Window : in out Button_Type) return Claw.Any_Window_Access_Type is begin return Window.My_Button'Unchecked_Access; end Get_Claw_Object;
which we can now do.
Conceptually, of course, the parameter to this function isn't even modified. We just need to use in out here to prevent someone from passing a constant, which could cause all manner of trouble.
!ACATS test
ACATS tests are needed to test that this is allowed, and that the restrictions on operator symbols are enforced. Moreover, existing ACATS B-Test tests prohibiting in out parameters on function needs to be withdrawn.
!appendix

From: Robert A. Duff
Sent: Saturday, November 9, 2002  11:36 AM

Robert Dewar said:

> As for Bob's :-( lament about not being able to build abstractions in Ada,
> I think that's overdrawn. Indeed there is a real danger in languages that
> allow too much syntactic abstraction, because then people start to essentially
> create sublanguages that are out of style with Ada.

My philosopy is:

    1. Make it easy to write good programs.
    2. Make it hard to write bad programs BY ACCIDENT.

but not:

    3. Make it hard to write bad programs.

The reason I reject number (3) is that it inevitably makes it hard to
write *good* programs as well.  For instance, number (3) may well
discourage "sublanguages that are out of style with Ada".  But it also
discourages the example you mentioned in an earlier message: making an
array-like abstraction.

A good example is the Unbounded_Strings package.  IMHO, it should be
possible to make the interface to that be just as clean as the interface
to Standard.String.  That requires allowing user-defined:

    literal notation
    indexing notation
    assignment
    operators (like "=" and "&")

Ada draws the line at an odd spot -- it allows the last item above,
but not the others.

It is true that these kinds of features allow programmers to write bad
programs, through stupidity, ignorance, or even malice.  But not "by
accident".  To redefine "+" to mean "-", you have to do it on purpose.

Note that I would not push this all the way.  For example, I am happy
that the syntax for 'if' and 'while' statements is built-in to Ada, and
cannot be changed by programmers.  It *could* be -- you can do that sort
of thing in Lisp.  In Lisp, you even have hooks into the lexical
analysis.  But that's going too far, IMHO.

> An example of doing this is GNAT.Spitbol.Patterns, but this is a very
> concious effort to create an abstraction that has a SNOBOL4 rather than
> an Ada flavor. I think that's not so bad in the case where you are mimicking
> an existing language.
>
> But suppose this package instead mimiced "dewars own not very well documented
> and not very well thoughout language xxx". Well that's not such a good idea
> at all.

True, but I don't want to make the Spitbol package more difficult to
write, or make its interface less clean, just because loose-canon Dewar
might foolishly define the horrible "language xxx" abstraction.
The solution must better education (teach that Dewar to be a better
programmer).  ;-)  I'm a language designer, not an educator,
so I have little to say about that.

> For example, suppose I allow := to be redesigned quite generally, and then,
> being a functional programming fan, I decide to make it's semantics equivalent
> to LET in scheme, rather than an assignment. I think that would be rather
> horrible :-)

Well, we *do* let you redefine := indirectly via Adjust, and you can
indeed redefine it to do crazy things.

P.S. Sorry for straying off topic.  I can't help but discuss
language-design philosophy, given that's it my main interest,
and I'm forced to make a living writing mere compilers.  ;-)

****************************************************************

From: Robert Dewar
Sent: Saturday, November 9, 2002  12:09 PM

> . Make it hard to write bad programs.

Well given that a lot of the Ada design philosophy is EXACTLY about this
I find this unacceptable as a general principle.

For example, we don't allow people to do Unchecked_Conversions without
saying a lot of stuff.

The fact of the matter is that most bad programming is NOT by accident,
it is by poorly-intentioned design. And yes, one function of the design
of a programming language is to try to disrupt this activity as much
as possible without getting too much in the way of legitimate stuff.

Certainly I agree with Bob in certain cases, I just don't accept it as
a general principle (once again, Bob, you seem to be stating this as a
general principle, and it is that generality I object to).

For example, suppose I introduce an attribute that can be applied to a
private type or object called 'Implementation, which reveals the underlying
implementation and allows the privacy barrier to be broken. That does not
make it easier to write bad programs by accident, no one can accidentally
type those 15 characters, but it sure makes it easier to write bad programs
by design, and although this pragma is indeed legitimate according to the
standard, it would represent a horrible attack on the integrity of the
language and I assume (hope?) that Bob would not be happy with it.

On the other hand, one of the things that the SPIBOL patterns implementation
has to work around (*) is the stupid rule that functions can't have IN OUT
parameters which to me is definitely in the category of trying to stop people
writing bad programs at the expense of all kinds of legitimate use (you see
here the problem is that I don't agree it is a terrible thing for functions
in Ada to have IN OUT parameters, in fact it seems rather natural for example
for a random number function to take a seeded object that is updated, rather
than have to mess with generic object nonsense.

So once again, I think that all three principles ennunciated by Bob are
not absolute, they represent extremes in the design space and you have to
find the best spot.

I incidentally find the Ada decisions of where to cut the abstraction quite
reasonable (they correspond incidentally to the choices made for Algol-68
and the reasoning is simlar -- parts of Ada were quite strongly inspired
by A68).

(*) what is done in the spitbol patterns implementation is to force the
relevant parameters to be passed by reference, then take the parameter
address by use of Unrestricted_Access, then to change the parameter by
assignment via the pointer, YUGH! But it works fine and presents a much
more spitbol like language

   the pattern  span (a) $ b

(which matches a bunch of a's and assigs the substring matched to b)

becomes

                span (a) * b

(pity I can't use $ as an operator :-)

****************************************************************

From: Robert A. Duff
Sent: Saturday, November 9, 2002  12:48 PM

> > . Make it hard to write bad programs.
>
> Well given that a lot of the Ada design philosophy is EXACTLY about this
> I find this unacceptable as a general principle.
>
> For example, we don't allow people to do Unchecked_Conversions without
> saying a lot of stuff.

I agree with that design decision.  I think it fits in with my
principle: you can't do it by accident.  It certainly doesn't stop
menaces from sprinkling U_C all over the place.  I've seen programs that
do that, and the extra typing work didn't stop the menace.

This decision also reflects another principle I didn't mention,
which I know you agree with:

    Force programmers to document what they're doing,
    especially when they're doing dangerous stuff.

This is the "readability over writeability" principle, which I've heard
you preaching in favor of.  Saying "with U_C" at the top of the file is
good documentation.

> The fact of the matter is that most bad programming is NOT by accident,
> it is by poorly-intentioned design. And yes, one function of the design
> of a programming language is to try to disrupt this activity as much
> as possible without getting too much in the way of legitimate stuff.

Well, I don't know about that.  I've seen many many bugs in my life, and
an awful lot of them were just silly little mistakes.  More in, say,
C than in, say, Ada.

> Certainly I agree with Bob in certain cases, I just don't accept it as
> a general principle (once again, Bob, you seem to be stating this as a
> general principle, and it is that generality I object to).

*This* time, I really meant it to be fairly general.  I'm sure there are
exceptions, but by and large, I think it is wise for the language
designer to avoid heavy-handed attempts to prevent stupid programmers
from doing stupid things.

You're a professor.  It's *your* job to educate new programmers,
not mine.  ;-)

Although I suppose there's always the School of Hard Knocks.  ;-)

> For example, suppose I introduce an attribute that can be applied to a
> private type or object called 'Implementation, which reveals the underlying
> implementation and allows the privacy barrier to be broken. That does not
> make it easier to write bad programs by accident, no one can accidentally
> type those 15 characters, but it sure makes it easier to write bad programs
> by design, and although this pragma is indeed legitimate according to the
> standard, it would represent a horrible attack on the integrity of the
> language and I assume (hope?) that Bob would not be happy with it.

I agree (I would not be happy with that feature).  I can use the
"readability" principle to back me up:  "is private" along with some
procedures is good documentation of an interface.  That property would
be destroyed if 'Implementation could be sprinkled all over.
Even if 'Implementation were not used in a given program,
it would still do damage, because the reader has to search all
over to know that fact.

> On the other hand, one of the things that the SPIBOL patterns implementation
> has to work around (*) is the stupid rule that functions can't have IN OUT
> parameters which to me is definitely in the category of trying to stop people
> writing bad programs at the expense of all kinds of legitimate use (you see
> here the problem is that I don't agree it is a terrible thing for functions
> in Ada to have IN OUT parameters, in fact it seems rather natural for example
> for a random number function to take a seeded object that is updated, rather
> than have to mess with generic object nonsense.

I've said before: I agree with you on the IN OUT parameters in functions
point.

> So once again, I think that all three principles ennunciated by Bob are
> not absolute, they represent extremes in the design space and you have to
> find the best spot.

Well, yes, I agree that no principle is absolute.
Including *this* one?  ;-)

> I incidentally find the Ada decisions of where to cut the abstraction quite
> reasonable (they correspond incidentally to the choices made for Algol-68
> and the reasoning is simlar -- parts of Ada were quite strongly inspired
> by A68).
>
> (*) what is done in the spitbol patterns implementation is to force the
> relevant parameters to be passed by reference, then take the parameter
> address by use of Unrestricted_Access, then to change the parameter by
> assignment via the pointer, YUGH!

This is a good example of a well-intentioned rule that actually damages
readability, because the workaround is uglier than just saying "in out".

By the way, if you make the thing tagged, you can say 'Unchecked_Access,
rather than 'Unrestricted_Access.  (Which I'm sure doesn't matter in
*your* case, since you control the compiler.)

Here's a similar trick:

    type T is limited private;
...
    type T limited
        record
            Self: T_Ptr := T'Unchecked_Access;

(presumably with a hefty commment explaining what's going on!)
Then a function with an 'in' parameter X of type T can say:

    Var: T renames X.Self.all;

and thereby evilly gain write access to X.  Yuck!  But it does solve the
problem.

>... But it works fine and presents a much
> more spitbol like language
>
>    the pattern  span (a) $ b
>
> (which matches a bunch of a's and assigs the substring matched to b)
>
> becomes
>
>                 span (a) * b
>
> (pity I can
> t use $ as an operator :-)

Yeah.  Using $ would be *more* readable.  Not just because it mimics the
Snobol syntax.  Even if you were designing this from scratch without
knowledge of Snobol, it's good to use an operator that does not cause
confusion with the standard "multiply" meaning.

One problem with allowing arbitrary operator symbols like $ is how to
define the precedence.  I've seen several error-prone solutions to that
in various languages.  I think I know a *good* solution, but this is a
case where I can fully understand JDI's reluctance to allow it.

****************************************************************

From: Robert Dewar
Sent: Saturday, November 9, 2002  5:18 PM

> > The fact of the matter is that most bad programming is NOT by accident,
> > it is by poorly-intentioned design. And yes, one function of the design
> > of a programming language is to try to disrupt this activity as much
> > as possible without getting too much in the way of legitimate stuff.
>
> Well, I don't know about that.  I've seen many many bugs in my life, and
> an awful lot of them were just silly little mistakes.  More in, say,
> C than in, say, Ada.

Well when I talk about "bad programming" I am not talking about bugs, but
about bad coding. There is lots of bad coding that is bug-free in the sense
that there are no functoinal errors and the code works.

I think it worth making a very strong distinction between design errors/bad
code, and bugs. It is the function of a language to provide an environment
in which both are harder to create while not unduly restricting what you
can do. For the most part (IN OUT parameters for functions are a signal
exception) I think Ada makes the right choices in this arena.

> One problem with allowing arbitrary operator symbols like $ is how to
> define the precedence.  I've seen several error-prone solutions to that
> in various languages.  I think I know a *good* solution, but this is a
> case where I can fully understand JDI's reluctance to allow it.

I assume you know the PRIORITY statement in Algol-68. But I am dubious
about this approach.

in fact I think it would be just fine to say that these arbitrary operator
symbols have no defined precedence and require parenthesization if mixed
with any other operators. After all the reader will have no intuition on
the precedence of $.

****************************************************************

From: Robert Dewar
Sent: Sunday, November 10, 2002  2:55 PM

> Go ahead and write up an AI.  ;-)

I would, except that I don't see anything has changed since the Ada9X
discussions. Both JDI and I strongly supported introducing one extra
user redefinable unary operator, to be used for conversion operations
(much as unary "+" is sometimes used now). The argument for this was
presented in I think as effective form as possible, but it was not
enough to win approval, so I guess it's collectively not viewed as a
good idea.

A similar sort of appraisal applies to IN OUT parameters for functions.

I don't think there is any point in re-introducing an issue that has
previously been discussed and resolved one way or another in the
context of the Ada 9X discussions unless there is new evidence to
bring to bare, or new experience. It is not good enough to simply
decide that the old decision was wrong and fight the same fight
again (I have that reaction to a number of proposals being
discussed for Ada 0Y).

****************************************************************

From: Alexander Kopilovitch
Sent: Sunday, November 10, 2002  5:21 PM

>> Go ahead and write up an AI.  ;-)
>
>I would, except that I don't see anything has changed since the Ada9X
>discussions. Both JDI and I strongly supported introducing one extra
>user redefinable unary operator, to be used for conversion operations
>(much as unary "+" is sometimes used now).

Perhaps, even a "stalled" AI for that might be useful: at least it is
interesting how such redefinable conversion operator may be introduced
without obviously wicked possibilities.

>A similar sort of appraisal applies to IN OUT parameters for functions.
>
>I don't think there is any point in re-introducing an issue that has
>previously been discussed and resolved one way or another in the
>context of the Ada 9X discussions unless there is new evidence to
>bring to bare, or new experience. It is not good enough to simply
>decide that the old decision was wrong and fight the same fight
>again (I have that reaction to a number of proposals being
>discussed for Ada 0Y).

Perhaps there is no need to fight, but those arguments for IN OUT parameters
for functions (in AI style, not comp.lang.ada) may be insteresting not only
for participants of those Ada 9X discussions. In particular, is there any
practical need for that, except of well-known need for procedures that return
result?

****************************************************************

From: Robert Dewar
Sent: Sunday, November 10, 2002  5:34 PM

> Perhaps, even a "stalled" AI for that might be useful: at least it is
> interesting how such redefinable conversion operator may be introduced
> without obviously wicked possibilities.

Feel free to submit one, I certainly will not :-)

> Perhaps there is no need to fight, but those arguments for IN OUT parameters
> for functions (in AI style, not comp.lang.ada) may be insteresting not only
> for participants of those Ada 9X discussions. In particular, is there any
> practical need for that, except of well-known need for procedures that return
> result?

Sure there are cases, I gave one (the random number example) but there are
may others, but again, it is pointless to restate the arguments, since these
have been thoroughly presented before, and it would just be treading well
known ground.

****************************************************************

From: Robert A. Duff
Sent: Sunday, November 10, 2002  6:09 PM

> I would, except that I don't see anything has changed since the Ada9X
> discussions.

I agree.  (You don't see *me* writing up that AI, either...)

But it's funny if you look at some of the AI's, and some of the
non-standard features supported by vendors, some of them were
dangerous, impossible to implement, and all manner of other bad
things in 1993, but somehow over time, people changed their collective
mind.  In some cases, what seemed like unnecessary bells and whistles
then, now seems like filling an obvious "hole".

>... Both JDI and I strongly supported introducing one extra
> user redefinable unary operator, to be used for conversion operations
> (much as unary "+" is sometimes used now). The argument for this was
> presented in I think as effective form as possible, but it was not
> enough to win approval, so I guess it's collectively not viewed as a
> good idea.

Well, the details matter.  For example, I can't get very excited about
the single "conversion" operator symbol, whereas a more general feature
would be of more interest to me.  (Assuming, for either feature, that
there was more general support -- I'm not going to push for either one.)

> A similar sort of appraisal applies to IN OUT parameters for functions.

That's a more important bug in the language design, but I agree there's
no point in continuing to push for it.  By the way, this is one of the
few language design decisions that Tucker and I disagreed on (and still
do).  ;-)

****************************************************************

From: Robert Dewar
Sent: Sunday, November 10, 2002  6:48 PM

Right, it's quite interesting. Although I have often disagreed with Tucker
on scope issues (objecting to features that seemed in context over complicated)
this is the only case where I have disagreed with him on a fundamental judgment
issue. very odd :-) :-)

****************************************************************

From: Alexandre E. Kopilovitch
Sent: Wednesday, November 13, 2002  12:11 PM

I'm not going to restate the arguments, but just want to know what is bad or
wrong in the following solution for a problem:

----------
Let us introduce new mode for formal parameter: RETURN mode. This RETURN mode
is the same as OUT mode except the following rules:

  in the procedure specification:

    1) only the first parameter of the procedure may be in RETURN mode;
    2) IN mode is incompatible with RETURN mode, that is, there is no IN RETURN
       mode.

  in the procedure body:

    if the formal parameter in RETURN mode is of indefinite type then the
    attributes, which depend on undefined properties (for example, 'First,
    'Length, etc.) cannot be used for that parameter.

  in a procedure call:

    1) a procedure with the first parameter in RETURN mode may be invoked as
       function (without argument for the first parameter);
    2) normal (that is, procedural) call for that procedure is also available;
       in that call RETURN mode is treated as OUT mode.

****************************************************************

From: Robert Dewar
Sent: Wednesday, November 13, 2002  11:17 PM

This is a warmed over version of the DEC Valued_Procedure pragmas ...

****************************************************************

From: Alexandre E. Kopilovitch
Sent: Thursday, November 14, 2002  10:35 AM

Well, of course, and GNAT implements those pragmas also. But this similarity
is a good thing, as it certifies for a proven mechamism. The difference is that
it is not an implementation-defined pragma(s), but a straight language form.

****************************************************************

From: Robert Dewar
Sent: Thursday, November 14, 2002  5:17 PM

Well I regard both the pragmas and the AK proposed substitute as too junky
to consider. If you want IN OUT parameters for functions, just allow them.
The DEC pragmas are merely a kludgy device to get around the absence of
a needed feature. I don't see the AK proposal here as any improvement over
the pragmas.

****************************************************************

From: Robert A. Duff
Sent: Thursday, November 14, 2002  6:07 PM

Robert Dewar said:

Well, "just allow them" would work for Ada 83.  But now Ada 95 has
protected functions, which have special status.  Do you think it makes
sense to allow 'in out' in protected functions, given their special
status as "multiple readers"?

I really think it would have been better to use some other syntax to
distinguish readers/writers of protected objects.  The
function/procedure distinction was a mistake, IMHO.  (As I said, one of
the *very* few technical disagreements between Tuck and me!)

Perhaps it was driven by the "new reserved word phobia" that plagued the
Ada 9X process?  I mean, the MRT was allowed to drag in all manner of
*semantic* complexity (see finalization, for example), but new syntax
was frowned upon, and new reserved words were anathema.  I never
understood that.

A related example: The MRT at one point proposed to allow "aliased" on
formal parameters.  Like "procedure P(X: aliased Some_Record);".  The
reviewers put the kibosh on that (new syntax, plus "alias" is an ugly
name used by criminals ;-)).  Later, we decided that *all* tagged
parameters are aliased, whether they want to be or not, and that rule
made it into the final Ada 95.  So I find myself making things tagged
just so I can have aliased parameters of the type, and I resent the
extra tag field.  Besides, I didn't want *all* parameters of that type
to be aliased.  Just some.

****************************************************************

From: Randy Brukardt
Sent: Thursday, November 14, 2002  6:44 PM

> Let us introduce new mode for formal parameter: RETURN mode.
> This RETURN mode is the same as OUT mode except the following rules:
>...

This basic idea might serve as a solution to the "constructor function"
problem that Bob Duff is working in AI-318. The problem with the existing
proposals for that were that the methods of getting a name for the return
result were just plain weird - objects with boatloads of restrictions. It
may be easier to define the needed restrictions in terms of a parameter
mode.

OTOH, I don't see any reason to use it as a back door to getting 'In Out'
parameters on functions. If that happens, it should be through the front
door. (I know that allowing 'In Out's on functions would make a real hash
out of the Janus/Ada optimizer and code generators, which use the 'no need
to access the parameters after the call' as a fundimental invariant. But I'm
still in favor of allowing them. As I've said previously, I was very against
the change in Ada 95, for the optimization/code generator reasons, so I can
call for reopening the issue. Which I have. The reason is that 'In Out'
parameters are fundemental to OOP, and 'Access' is a poor substitute (as the
fact that the parameter is access is visible at the call site).

****************************************************************

From: Robert Dewar
Sent: Thursday, November 14, 2002  9:30 PM

> Well, "just allow them" would work for Ada 83.  But now Ada 95 has
> protected functions, which have special status.  Do you think it makes
> sense to allow 'in out' in protected functions, given their special
> status as "multiple readers"?

True, this nasty mistake in the Ada 95 design (one if you remember I
strongly opposed) make the change more difficult.

> I really think it would have been better to use some other syntax to
> distinguish readers/writers of protected objects.  The
> function/procedure distinction was a mistake, IMHO.  (As I said, one of
> the *very* few technical disagreements between Tuck and me!)

Abnsolutely! This is part and parcel of the same odd technical position :-)

> A related example: The MRT at one point proposed to allow "aliased" on
> formal parameters.  Like "procedure P(X: aliased Some_Record);".  The
> reviewers put the kibosh on that (new syntax, plus "alias" is an ugly
> name used by criminals ;-)).  Later, we decided that *all* tagged
> parameters are aliased, whether they want to be or not, and that rule
> made it into the final Ada 95.  So I find myself making things tagged
> just so I can have aliased parameters of the type, and I resent the
> extra tag field.  Besides, I didn't want *all* parameters of that type
> to be aliased.  Just some.

I would find it difficult to live without 'Unrestricted_Access :-)

****************************************************************

From: Michael F. Yoder
Sent: Friday, November 15, 2002  10:23 AM

The decision could be escaped in an upwardly compatible manner by making
readerhood and writerhood be default conditions that could be
overridden. (I won't suggest any syntax.) And if 'out' and 'in out'
parameters were allowed in functions, the default could be to make only
functions lacking such parameters be readers. (That would seem kludgy to
me, but it would be compatible with legacy code and wouldn't require
adding extra syntax or pragmas to override the defaults.)

****************************************************************

From: Robert Dewar
Sent: Friday, November 15, 2002  10:34 AM

Possibly so. The real point here is that I see no new arguments and no need
to revisit the issue. Given that there is nothing new, and that it has been
discussed to death before, and there has never been even a simple majority
let alone a consensus in favor of IN OUT parameters for functions, I think
it is a dead issue.

****************************************************************

From: Alexandre E. Kopilovitch
Sent: Friday, November 15, 2002  3:22 PM

>OTOH, I don't see any reason to use it as a back door to getting 'In Out'
>parameters on functions. If that happens, it should be through the front
>door.

I believe that both these doors - front and back - should be present because
each of them has its own use.
  For example, for a subroutine (say, within a bindings) that returns error
code I prefer valued procedure. But for random number generator I certainly
prefer function with IN OUT parameter.

****************************************************************

From: Robert Dewar
Sent: Friday, November 15, 2002  5:11 PM

Ah, the irresistable taste for features and more features :-)

Anyway, I repeat at this stage I would oppose any change in this area, because
I do not believe it can possibly get consensus support.

****************************************************************

From: Florian Weimer
Sent: Friday, November 15, 2002  4:31 PM

> Well, "just allow them" would work for Ada 83.  But now Ada 95 has
> protected functions, which have special status.  Do you think it makes
> sense to allow 'in out' in protected functions, given their special
> status as "multiple readers"?

Why would that be a problem?  If the mode is "out" or "in out", this
doesn't automatically imply that the function changes the state of the
protected object.  Or am I missing something?

****************************************************************

From: Robert A. Duff
Sent: Friday, November 15, 2002  4:45 PM

I'm not sure.  Sometimes PO's protect data that is not inside the PO.

****************************************************************

From: Robert Dewar
Sent: Friday, November 15, 2002  5:25 PM

Well no of course it doesn't, but it makes this non-orthogonal and rather
weird treatment fo functions in protected objects one notch more peculiar
if you admit that functions really aren't mathematical functions :-)

****************************************************************

From: Robert Dewar
Sent: Friday, November 15, 2002  5:28 PM

> I'm not sure.  Sometimes PO's protect data that is not inside the PO.

Sure, but functions aren't really functions in Ada, no matter how much
some people :-) would like to think that. They are procedures with

a) a silly restriction on IN OUT parameters
b) a method of returning a result

They can do anything they want by calling other routines, messing with
address clauses, applying 'Access to their tagged parameters, modifying
things pointed to by access parameters etc. So allowing IN OUT parameters
does not actually change things.

The rule in Ada is quite simple. Functions are allowed to have arbitrary
side effects, but are not allowed to indicate any such side effects int
the specification :-)

****************************************************************

From: Alexandre E. Kopilovitch
Sent: Sunday, November 17, 2002  11:42 AM

>I do not believe it can possibly get consensus support.

Lacking a list of arguments against IN OUT in functions, I only guess that
perhaps following restrictions will satisfy the opponents:

1) function with IN OUT parameter cannot be operator (and cannot be renamed
   to operator)
2) actual argument for IN OUT formal parameter cannot be used in any place
   within the whole statement.

>functions aren't really functions in Ada, no matter how much
>some people :-) would like to think that.

Well, even in pure mathematics functions sometimes have effectively OUT
parameters - when used in equations (with a suitable form of equation it is
possible to simulate IN OUT parameter also).

****************************************************************

From: Robert Dewar
Sent: Sunday, November 17, 2002  11:48 AM

You guess completely wrong, you should really make an effort to dig up
the old threads on this subject. There is no point in wasting time going
over an argument again just because a new arguer has arrived on the scene :-)

The objection is that IN OUT parameters are fundamentally inappropriate
for functions.

****************************************************************

From: Matthew Heaney
Sent: Monday, November 18, 2002  5:12 PM

> But for random number generator I certainly
> prefer function with IN OUT parameter.

If the full view of the type is limited, you don't need inout parameters.

The predefined random number Generator type works just fine, without inout
params.  (My only complaint is that that package isn't pure.)

If your only concern is being able to have side effect for functions, then
inout params aren't necessary (if the type is limited).

****************************************************************

From: Robert A. Duff
Sent: Monday, November 18, 2002  6:24 PM

> If the full view of the type is limited, you don't need inout parameters.

Only via ugly and inefficient kludgery.

> The predefined random number Generator type works just fine, without
> inout params.  (My only complaint is that that package isn't pure.)

I don't see how the package could be Pure.  If it were, then:

    X: T := Random(...);
    Y: T := Random(...);

the compiler would be allowed to optimize away one of the calls,
thus making X = Y, which is clearly *not* what random number generators
are supposed to do!

> If your only concern is being able to have side effect for functions,
> then inout params aren't necessary (if the type is limited).

My concern is to document the 'in out'-ness in the spec of the function.
Also, I don't like the "limited" restriction.  Sometimes I make a
conceptually-limited type nonlimited, purely to enable aggregates (which
ought to be allowed for limited types, but are not -- see AI-287).

By the way, making it tagged can also work.

****************************************************************

From: Robert Dewar
Sent: Monday, November 18, 2002  9:07 PM

You had better post code. I can't guess what you mean by "works just fine".

****************************************************************

From: Robert A. Duff
Sent: Tuesday, November 19, 2002  8:09 PM

The GNAT version says:

>    --  The design of this spec is very awkward, as a result of Ada 95 not
>    --  permitting in-out parameters for function formals (most naturally
>    --  Generator values would be passed this way). In pure Ada 95, the only
>    --  solution is to use the heap and pointers, and, to avoid memory leaks,
>    --  controlled types.
>
>    --  This is awfully heavy, so what we do is to use Unrestricted_Access to
>    --  get a pointer to the state in the passed Generator. This works because
>    --  Generator is a limited type and will thus always be passed by reference.

The part about "awkward" is correct, but the part about the "only way"
being heap usage is wrong.  I don't know what Matthew Heaney had in
mind, but the trick I sometimes use is this:

In private part of random numbers package:

    type Generator_Ptr is access all Generator;
    type Generator is limited
	record
	    Self: Generator_Ptr := Generator'Unchecked_Access;
	    ... other fields
	end record;

In body:

    function Random(Gen: Generator) return ... is
        Variable_Generator: Generator renames Gen.Self.all;
    begin
        Variable_Generator.Something := ...;
        ...
    end Random;

Variable_Generator is a writeable view of Gen.

Whether this is, as Matthew claims, "just fine", is a matter of opinion,
I suppose.  It is at least pure, portable, Ada 95.

I don't like it because:

    - It's tricky.

    - It requires the type to be limited -- the 'Unchecked_Access would
      be illegal otherwise.

    - It wastes a word of storage.  That's fine in this case (I suspect
      the average number of Generator objects in programs that use
      random numbers is approximately 1).  But if I'm creating a million
      Things, and each is 2 words, I don't want to add an extra Self
      field to type Thing.

    - (Most importantly) The spec of function Random is a lie!

Apparently, the author of the GNAT version didn't know about this trick.
Apparently, the MRT didn't know about it either, when the RM was
written, because the AARM suggests a heap-based implementation.  In
fact, that AARM comment was wrong; the version-with-corrigendum fixes
it (to a different heap-based implementation).

****************************************************************

From: Robert Dewar
Sent: Tuesday, November 19, 2002  8:32 AM

But the GNAT code is clearly superior, because it avoids the extra word
by a (safe) use of unrestricted access. The GNAT run-time definitely is
not written with portability to other GNAT compilers in mind :-)

****************************************************************

From: Robert A. Duff
Sent: Tuesday, November 19, 2002  8:44 AM

It is perhaps be superior as a part of GNAT, but that's irrelevant in a
discussion of the Ada language (as opposed to the GNAT dialect of Ada).

Of course, the 'Unrestricted_Access attribute can easily be replaced by
an Unchecked_Conversion.  I still prefer the "Self" trick, unless you're
going to be creating a lot of objects of the type.

And *all* of these tricks, including the 'Unrestricted_Access, are
inferior to simply saying "in out".  Except for one minor flaw -- it's
illegal.  ;-)

****************************************************************

From: Robert Dewar
Sent: Tuesday, November 19, 2002  8:54 AM

> It is perhaps be superior as a part of GNAT, but that's irrelevant in a
> discussion of the Ada language (as opposed to the GNAT dialect of Ada).

Well you commented that the GNAT author's apparently did not know the trick,
but in fact we did and conciously preferred the implementation we have :-)

> And *all* of these tricks, including the 'Unrestricted_Access, are
> inferior to simply saying "in out".  Except for one minor flaw -- it's
> illegal.  ;-)

Indeed :-)

> Of course, the 'Unrestricted_Access attribute can easily be replaced by
> an Unchecked_Conversion.  I still prefer the "Self" trick, unless you're
> going to be creating a lot of objects of the type.

Yes, that's always the case, for pointer types *other* than pointers to
unconstrained arrays, the Unrestricted_Access attribute is simply equivalent
to 'Address followed by an unchecked conversoin.

However, for pointers to unconstrained arrays, this attribute is much more
interesting :-) For example, you can create a pointer to a slice with
proper bounds. That of course is *very* implementation dependent (it
depends on the use of fat pointers).

****************************************************************

From: Matthew Heaney
Sent: Tuesday, November 19, 2002 10:32 AM

> > If the full view of the type is limited, you don't need
> inout parameters.
>
> Only via ugly and inefficient kludgery.

I don't know if this implementation falls into the "ugly and inefficient"
category, but here it is:

package Random_Numbers is

   pragma Pure;

   type Generator is limited private;

   function Random (G : Generator) return Float;

private

   type State_Type is record ... end record;

   type Handle (G : access Generator) is
      limited null record;

   type Generator is
     limited record
        H : Handle_Type (Generator'Access);  --per Jean-Pierre
        State : State_Type;
     end record;

end Random_Numbers;

Now you can implement function Random like this:

  function Random (G : Generator) return Float is
     State : State_Type renames G.H.G.State;
  begin
     <modify variable view State as necessary>
     return Result;
  end;

> > The predefined random number Generator type works just fine, without
> > inout params.  (My only complaint is that that package isn't pure.)
>
> I don't see how the package could be Pure.

Because there are no access types.

> If it were, then:
>
>     X: T := Random(...);
>     Y: T := Random(...);
>
> the compiler would be allowed to optimize away one of the calls,
> thus making X = Y, which is clearly *not* what random number
> generators > are supposed to do!

Well then something is amiss, because the code fragment above will compile just
fine.

> > If your only concern is being able to have side effect for functions,
> > then inout params aren't necessary (if the type is limited).
>
> My concern is to document the 'in out'-ness in the spec of
> the function.

But not all abstractions work that way -- specifically, the random number
package is *not* modelled that way.  This is the same for the predefined
packages, which use mode in (not inout) for Read and Write.

The model is that the type is really a "handle," which designates some
modifiable, changing state, but which itself (the handle) is constant.  Hence
only mode in is required.

In the code fragment above, the "handle" and the "modifiable state" are
allocated immediately adjacent to one another, on the stack.  (This view of
things is per Tucker.)

If you don't like that model, then you could argue that there "physical" state
changes that are not part of the "logical" model of the abstraction.  For
example, if you wanted to instrument a type to keep track of how many times a
certain operation (that otherwise does not modify the object) had been called.

In C++ you can do this using the "mutable" keyword.  The problem with Ada95 is
that the compiler has to infer this from the nature of the type (ie implemented
using the Rosen Trick, as above, or it's Volatile), rather than being told
explicitly be the programmer.

> Also, I don't like the "limited" restriction.  Sometimes I make a
> conceptually-limited type nonlimited, purely to enable aggregates (which
> ought to be allowed for limited types, but are not -- see AI-287).

Agreed.

> By the way, making it tagged can also work.

Yes, I knew that, but I wasn't sure what the effect would be if you were to
modify a pass-by-reference, nonlimited object through the back door (via some
Chap13 mechanism), because nonlimited objects can be declared as constant.  For
example, if we have this:

   type T is tagged record ... end record;

   function Op (O : T) return T2;

and then we do this:

   O : constant T := <whatever>;

   X : T2 := Op (O);

What should happen if Op modifies object O?  I was nervous about O being
allocated in a read-only part of memory, because it is marked as constant.  For
limited types, I worry less because limited objects can't be declared as
constant.

Again, C++ solves that problem using the "mutable" keyword, which lets the
compiler know that objects of this types can't be allocated in read-only
memory.

****************************************************************

From: Matthew Heaney
Sent: Tuesday, November 19, 2002 10:44 AM

> You had better post code. I can't guess what you mean by
> "works just fine".

I did -- 3 years ago, on CLA.  Here was your response:


From: Robert Dewar <robert_dewar@my-deja.com>
Subject: Re: Array of Variant Records Question...
Date: 1999/09/17

In article <37e24575@news1.prserv.net>,
  "Matthew Heaney" <matthew_heaney@acm.org> wrote:
> So there is in fact an Ada95 locution for "casting away const," a technique
> I've been calling "The Rosen Trick."

Yes, that's a LOT cleaner than the way that GNAT does it now,
though a bit less efficient (GNAT just modifies its parameter
directly and does not need the pointer).

We probably won't change it since there is no particular point
in throwing away the efficiency, especially when it is not
broke :-)

However, the heap method is clearly junk compared to this!

****************************************************************

From: Robert Dewar
Sent: Tuesday, November 19, 2002 10:46 AM

Well it certainly falls into the OBE category (see previous messages in
the thread :-)

****************************************************************

From: Robert A. Duff
Sent: Tuesday, November 19, 2002 11:18 AM

What's OBE?

****************************************************************

From: Matthew Heaney
Sent: Tuesday, November 19, 2002 11:33 AM

I assume he meant "overcome by events."  (Although I must admit that I am
somewhat nonplussed by this answer, because I thought the categorization of
random number generator packages had *not* been discussed in this thread.)

****************************************************************

From: Robert Dewar
Sent: Tuesday, November 19, 2002 12:22 PM

OBE = Overtaken By Events

(a standard internet abbreviation like CULTR :-)

****************************************************************

From: Michael Feldman
Sent: Tuesday, November 19, 2002  5:55 PM

When I read this I was ROTFL.

****************************************************************

From: Matthew Heaney
Sent: Tuesday, November 19, 2002 11:09 AM

> The part about "awkward" is correct, but the part about the "only way"
> being heap usage is wrong.  I don't know what Matthew Heaney had in
> mind,

I had the "Rosen Trick" in mind.


> but the trick I sometimes use is this:
>
> In private part of random numbers package:
>
>     type Generator_Ptr is access all Generator;
>     type Generator is limited
> 	record
> 	    Self: Generator_Ptr := Generator'Unchecked_Access;
> 	    ... other fields
> 	end record;
>
> In body:
>
>     function Random(Gen: Generator) return ... is
>         Variable_Generator: Generator renames Gen.Self.all;
>     begin
>         Variable_Generator.Something := ...;
>         ...
>     end Random;
>
> Variable_Generator is a writeable view of Gen.

You can do accomplish the same effect without a named access type, as I showed
in my previous email message, ie

  type Handle_Type (G : Generator) is limited null record;

  type Generator is
     limited record
         H : Handle_Type (G'Access);
         ...other fields
     end record;

And now G.H.G.all is a variable view.  No named access types are required.

> Whether this is, as Matthew claims, "just fine", is a matter of opinion,
> I suppose.  It is at least pure, portable, Ada 95.

But your method requires named access types.  Jean-Pierre's does not.  This
affects whether the package can be declared using a Pure categorization.

You and I discussed this 3 years ago, on CLA:

From: Robert A Duff <bobduff@world.std.com>
Subject: Re: Array of Variant Records Question...
Date: 1999/09/17

"Matthew Heaney" <matthew_heaney@acm.org> writes:

> Clearly, by using the Rosen Trick, this heavy implementation isn't
> necessary.  You have to wonder why the AARM described the heap-based
> solution, instead of the lighter (simpler?) internal handle idiom.

Well, *I* didn't think of it at the time.



> I don't like it because:
>...
>     - (Most importantly) The spec of function Random is a lie!

Again, it depends on what model you're using.  Not all state changes need be
advertised in the public part of the spec, because in the "logical" view of the
type there is no state change.

The problem is that parameter modes ("in" vs. "inout") in Ada conflate these
two views ("logical" vs. "physical") of the abstraction.  This is not unlike
the problem with overriding operators for a type; it would be better if the
public and private views of a type were in separate namespaces.  For example,
if I do this:

   type T is private;

   function "+" (L, R : T) return T;

private

   type T is new Integer;

The problem here is that I've lost access to the predefined "+" for type T.
This often trips new Ada programmers, who will try to implement the overriden
"+" operator using the predefined operator, and end up with infinite recursion
instead.

The real issue is that there should be different ways to communicate
information to the compiler vs communicate information to the programmer using
the abstraction.  You, Bob Duff, want a random number generator to announce its
state change, but that is the compiler-writer in you speaking.  For a
programmer (like me) who needs to use a random number generator, I don't really
care whether the function is "in" or "inout" because the syntax is the same.

(Actually, this was Jean's motivation for *not* allowing the parameter-passing
mode to be used in overload resolution, because it's not obvious at the point
of call which operation is being invoked.  This is explained in the Ada83
Rationale.)

> Apparently, the author of the GNAT version didn't know about this trick.
> Apparently, the MRT didn't know about it either, when the RM was
> written, because the AARM suggests a heap-based implementation.  In
> fact, that AARM comment was wrong; the version-with-corrigendum fixes
> it (to a different heap-based implementation).

I hope the Rosen Trick is in there somewhere.  I consider it superior to your
technique of using assignment to a named access type.

I still maintain that the language should give implementors freedom to declare
the random number generator packages as Pure.  (I would like it even better if
the RM required Pure.)

****************************************************************

From: Robert A. Duff
Sent: Tuesday, November 19, 2002 11:28 AM

Matthew Heaney said:

> I don't know if this implementation falls into the "ugly and
> inefficient" category, but here it is: [...access discrim method]

It's uglier than "in out", but prettier than the 'Unchecked_Access
method I posted.  It's probably the same efficiency as the method
I posted.

> > If it were, then:
> >
> >     X: T := Random(...);
> >     Y: T := Random(...);
> >
> > the compiler would be allowed to optimize away one of the calls,
> > thus making X = Y, which is clearly *not* what random number
> > generators
> > are supposed to do!
>
> Well then something is amiss, because the code fragment above will
> compile just fine.

Well, I think what I wrote above (X could = Y) is wrong, given the exact
wording of 10.2.1(18).  I'm not sure whether something is amiss w.r.t.
Pure, and I don't want to think about it too hard right now.  ;-)

> The model is that the type is really a "handle," which designates some
> modifiable, changing state, but which itself (the handle) is constant.
> Hence only mode in is required.

I suppose...

> > By the way, making it tagged can also work.

I take that back.  Making it tagged makes the parameter aliased,
but not writeable.

> Yes, I knew that, but I wasn't sure what the effect would be if you
> were to modify a pass-by-reference, nonlimited object through the back
> door (via some Chap13 mechanism), because nonlimited objects can be
> declared as constant.  For example, if we have this:
>
>    type T is tagged record ... end record;
>
>    function Op (O : T) return T2;
>
> and then we do this:
>
>    O : constant T := <whatever>;
>
>    X : T2 := Op (O);
>
> What should happen if Op modifies object O?

I think it's erroneous.

****************************************************************

From: Matthew Heaney
Sent: Tuesday, November 19, 2002 11:30 AM

> Bob Duff said:
> > Apparently, the author of the GNAT version didn't know
> > about this trick.
>
> And Robert Dewar responded:
> But the GNAT code is clearly superior, because it avoids the extra word
> by a (safe) use of unrestricted access.

Yes, but even with the GNAT technique you still can't declare the package as
Pure (because of the presence of named access types).

One issue (perhaps this does not have "problem" status) is that the compiler
must infer whether objects of the type are modified by looking at how the
package body is implemented, rather than by simply looking at the declaration
in the spec.

At least in C++, the programmer can mark the full view of the type as
"mutable", which is perfectly unambiguous.  And you can use it for nonlimited
types.

****************************************************************

From: Robert Dewar
Sent: Tuesday, November 19, 2002 12:22 PM

Incidentally, in the GNAT world, we frequently use Unrestricted_Access to
get around lack of IN OUT parameters, and yes, its ugly, but it works :-)

****************************************************************

From: Tucker Taft
Sent: Tuesday, November 19, 2002  9:02 AM

An interesting ramification of the proposed
amendment allowing aggregates for limited
type initialization is that it would now
be possible to have limited *constants*.
Those don't exist in Ada 83 or Ada 95 (unless
the full type is non-limited).

This implies that tricks that treat a
limited "in" object as a variable are in
jeopardy of doing even more harm.

Conceivably the trick that uses
'unchecked_access to generate a
self-pointer could be disallowed
for constants.  What this effectively
means is that the programmer could not
use "<>" for the self-pointer component
of the aggregate -- they would have to
provide some explicit value (which would
necessarily not be a self-pointer).

Note that controlled objects are already
a bit "funny" in this sense, since the
Finalize routine is allowed to write on
the object, even if it is a constant.
However, in this case, the object is
at least constant throughout its "normal"
lifetime.

****************************************************************

From: Robert Dewar
Sent: Tuesday, November 19, 2002  6:20 PM

Obviously such low level techniques have to be used carefully, just like
Unchecked_Conversion. After all UC can do unlimited harm also, let's keep
things in perpsective :-)

****************************************************************

From: Alexander Kopilovitch
Sent: Tuesday, November 19, 2002  1:56 PM

>> perhaps following restrictions will satisfy the opponents:
>>
>> 1) function with IN OUT parameter cannot be operator (and cannot be renamed
>>    to operator)
>> 2) actual argument for IN OUT formal parameter cannot be used in any place
>>    within the whole statement.
>
>You guess completely wrong,

Not so completely - it appears that the above restrictions actually address
some of those old objections.

> you should really make an effort to dig up
>the old threads on this subject.

I just did that. I found 5 files in old Ada-Comment archive:

   http://archive.adaic.com/standards/95com/mrtcomments/

namely: 94.0817, 94.0818, 94.0819, 94.0821, 94.0907, relevant to the OUT
parameters for functions. I assume that those files cover that old discussion
(if I missed something important and there are other relevant files then I
hope somebody will point me at them).

  It appears that most often stated objection against lifting the restriction
(OUT/IN OUT parameters for functions) was simply "too late" (meaning some
schedule of the Ada9X project) - I found 3 persons saying that. Obviously,
today that kind of objection is "void and null".

  Among the essential objections some are fully covered by the proposed
restrictions (above). Another is about totally unspecified fantom danger
(it is about overloading). And only one seems still valid: it is about
complex expressions where a function call may be used as an actual argument
for another function. I believe that that objection will be covered by the
following 3rd rule for IN OUT parameters for functions:

3) in a function call, an actual argument that corresponds to IN OUT formal
   parameter must be a variable, and must be preceeded by the keyword VAR.

For example:

  X := Random(var Y);

With this rule any confusion becomes improbable, and readability flourishes.

>The objection is that IN OUT parameters are fundamentally inappropriate
>for functions.

Apparently that "fundamentally inappropriate" disintegrates into several
objections, and they are covered by the proposed 3 rules - I repeat them here
together:

1) function with IN OUT parameter cannot be operator (and cannot be renamed
   to operator)
2) actual argument for IN OUT formal parameter cannot be used in any place
   within the whole statement.
3) in a function call, actual argument that corresponds to IN OUT formal
   parameter must be a variable, and must be preceeded by the keyword VAR.

****************************************************************

From: Robert Dewar
Sent: Tuesday, November 19, 2002  6:27 PM

>   It appears that most often stated objection against lifting the restriction
> (OUT/IN OUT parameters for functions) was simply "too late" (meaning some
> schedule of the Ada9X project) - I found 3 persons saying that. Obviously,
> today that kind of objection is "void and null".

Not obvious at all. At this stage, any changes to Ada have to meet a very
heavy burden of value to implementation difficulty. For instance at this
stage, implementing IN OUT parameters for functions in GNAT would be a
major effort, and it would not surprise me if the same was true of other
compilers.

I know that there is a kind of "hobbyist" enthusiasm for adding all sorts
of neat features to Ada. Even though many of these proposals may have
technical merit, that does not mean at all that it is feasible to add all
these features.

What we want for a revision (if there is indeed a revision, that is by no
means certain yet), is a small selection of very high value features, where
the value to implementation effort is high and the disruption is minimum.
Personally I don't think that IN OUT modes for parameters even vaguely
*begin* to meet that criterion at this stage.

Remember that I think that purely from a technical point of view, this
would be a good addition, but there are hundreds of equally worthy minor
"improvements", almost none of which are likely to make the cut.

****************************************************************

From: Robert A. Duff
Sent: Tuesday, November 19, 2002  6:43 PM

I agree with Robert Dewar's comments.

****************************************************************

From: Robert Dewar
Sent: Tuesday, November 19, 2002  6:48 PM

> I know that there is a kind of "hobbyist" enthusiasm for adding all sorts
> of neat features to Ada. Even though many of these proposals may have
> technical merit, that does not mean at all that it is feasible to add all
> these features.

Just so I don't offend anyone, I certainly regard myself as a "hobbyist"
when it comes to programming languages (I was teaching SPITBOL to my
undergraduate students today, and it is always fun to be back in an
environment of neat stuff :-) But just because features in a language
seem neat and useful is not nearly enough reason to implement them.

I have a rule that says that every feature you add to a language damages
the language. You have to be very careful that the added value exceeds
the damage.

If you look at Ada 9X mapping document II, you will find all sorts of
very nice features, e.g.

  generalized extensible exception hierarchies
  class types for non-tagged types
  lots of other neat stuff

but unfortunately we did not have room for them in Ada 95 without making
the language too large and too complex. It is not at all clear that we
have significantly more room for added complexity now than we did then.

I must say that in my experience talking with real Ada programmers, I have
very seldom got the impression that people choose languages other than Ada
because of lack of features in Ada. Occasionally, they do miss the
availability of particular libraries etc.

But the availability of libraries depends on the effort put in to generate
these libraries. Yes, some improvement in abstraction mechanisms makes it
marginally easier to write such libraries, but I think this is completely
minor compared with finding the effort to design and produce the libraries
in the first place.

****************************************************************

From: Robert I. Eachus
Sent: Tuesday, November 19, 2002  1:43 PM

"A tale, told by an idiot, full of sound and fury. Signifying nothing..."

I'm sorry, I figure I have to inject a note of sanity in here.  When
working on a random number package for Ada 9X, the issues of providing
implicit generators, on a per thread or per partition basis was
discussed at length, as well as providing a protected interface to
default generators, versions of the Random function with an in out
parameter (possibly as a procedure) and so on.

As I recall, the decision that generators should be passed using mode in
was finalized just before the final vote on allowing in out parameters
for functions.  As I recall, the reasons for doing so had little to do
with the legality of in out parameters for functions, and everything to
do with how to properly use the standard package from more than one
task.  Having the generator passed as mode in makes doing the right
thing in a tasking environment easy--you can either have a per task
attribute which is the generator for that task, or create a protected
object that contains a generator, depending on what you want.

So please, please, if you must continue arguing about in out parameters
for functions, leave random number generators out of it.  Passing
generators implicitly or explicity as in out parameters doesn't work.
 All of the "tricks" discussed here however compose with tasking just
fine.  It also could have been done by making the parameter of Random be
an explicit access parameter, but that was not done for aesthetic reasons.

****************************************************************

From: Matthew Heaney
Sent: Tuesday, November 19, 2002  6:11 PM

Well, one problem with the existing schema is that the compiler lets you do
this:

   protected Protected_Generator is

      function Random return Float;

   private

      G : Generator;

   end;

   protected body Protected_Generator is

      function Random return Float is
      begin
         return Random (G);
      end;

   end;

Isn't this broken?  The problem is that the compiler is allowed to return the
same value for the protected function Random, and if multiple tasks happen to
be calling Protected_Generator then they can all get the same random number
value.

What you have to do is extract the random number from Protected_Generator using
a protected procedure, not a protected function, and then all is well.  But
this locution is hardly obvious.

So unless my understanding of the interaction of protected objects and the
random number generator is wrong, then I think it's being generous to
characterize doing the right thing as "easy."

****************************************************************

From: Robert Dewar
Sent: Tuesday, November 19, 2002  6:29 PM

I find this peculiar and incomprehensible. The approach used in GNAT is
absolutely equivalent at the implementation level to allowing IN OUT
parameters to functions.

****************************************************************

From: Robert Dewar
Sent: Tuesday, November 19, 2002  6:29 PM

> Isn't this broken?  The problem is that the compiler is allowed to
> return the same value for the protected function Random, and if multiple
> tasks happen to be calling Protected_Generator then they can all get the
> same random number value.

I don't see this as other than an obvious bug in the program.

****************************************************************

From: Matthew Heaney
Sent: Tuesday, November 19, 2002  6:55 PM

I'm sure that you, Robert Dewar, see this as an "obvious" bug.  But that
doesn't mean it's obvious!

To me the most obvious way to protect a generator is using the implementation I
presented.  I know it's wrong, now, but I had to learn the hard way...


****************************************************************

From: Robert Dewar
Sent: Tuesday, November 19, 2002  7:03 PM

Well what caught you out was the distinction between functions (read locks)
and procedures (write locks), which is bizarre distinction that can indeed
cause confusion.

But I am surprised, are there in fact compilers that bother with read locks
on functions. Certainly GNAT does not (there is no obvious way to do this
with interface to typical posix thread systems).

So what compiler did you learn the hard way on :-)

****************************************************************

From: Robert I. Eachus
Sent: Wednesday, November 19, 2002  1:06 AM

>What you have to do is extract the random number from Protected_Generator
>using a protected procedure, not a protected function, and then all is well.
>But this locution is hardly obvious.
>
>So unless my understanding of the interaction of protected objects and the
>random number generator is wrong, then I think it's being generous to
>characterize doing the right thing as "easy."

I didn't say obvious, I said easy.  ;-)  Of course the body of a package
that shares RNGs between tasks has to contain a (protected) procedure
call, not a function call, to the "real" generator object.  But it is
easy to write that package in a generator independent way, and even make
it a generic.  What having the generator as an in parameter forces is
that the state of the actual value passed can't be changed by the RNG.
It doesn't matter whether the implementation uses the "Rosen trick" or
some other method to come up with a constant pointer that is the real
value passed in the call , or some other method.  The point is that you
can have a user visible function that is not a task entry or call on a
protected object.  All that can be hidden out of sight.

Of course, I have to wonder why anyone would want to share a generator
in that fashion, but it can be done.  Much better in almost any real
application I have run into is to have one independent generator per
task--for most discrete event simulations the only sensible approach
even if the different objects that use the generators are in the same
task.  You don't want the generator to result in objects that should be
independent becoming interdependent, especially in discrete event
simulations.  When you do want to dispatch tasks with one or more random
parameters,  by far the most usual case is that you have a dispatcher
task that passes out work to a number of separate processors.  In this
case you almost always want the results to be independent of the number
of processors used.  (But in every case where I have done so, the
correct "magic" was to use a generator function that could be advanced
quickly to a future value.  Each actual working task is given N values
per rendezvous with the dispatcher, and call the dispatcher again when
those values are used up.  When this approach is correct, it allows you
to spread for example, a genetic algorithm over many processors, and get
results indendent of the number of processors used.)

****************************************************************

From: Dan Eilers
Sent: Wednesday, January 15, 2003  6:00 PM

On 19 Nov 2002, Matthew Heaney showed the clever "Rosen trick"
for "casting away const".  I've found a simpler way that works
at least on Gnat, but alas doesn't seem to work on all compilers.

-- Rosen trick:

package Random_Numbers is

   pragma Pure;

   type Generator is limited private;

   function Random (G : Generator) return Float;

private

   type State_Type is record i: integer := 0; end record;

   type Handle (G : access Generator) is
      limited null record;

   type Generator is
     limited record
        H : Handle(Generator'Access);  --per Jean-Pierre
        State : State_Type;
     end record;

end Random_Numbers;


package body random_numbers is
  procedure newstate(x: in out State_type) is
  begin
     x.i := x.i + 1;    -- just increment for now
  end;

  function Random (G : Generator) return Float is
     State : State_Type renames G.H.G.State;
  begin
     newstate(State);
     return float(State.i);     -- just return state for now
  end;
end random_numbers;

with random_numbers;
with text_io;
procedure main is
   g: random_numbers.generator;
begin
   for i in 1..10 loop
     text_io.put_line(float'image(random_numbers.random(g)));
   end loop;
end;

------------------------------------------------------------------------

-- New way:


package Random_Numbers is

   pragma Pure;

   type Generator is limited private;

   function Random (G : Generator) return Float;

private

   type State_Type is record i: integer := 0; end record;

   type Generator is tagged limited record
        State : State_Type;
   end record;

end Random_Numbers;


package body random_numbers is
  procedure newstate(x: in out State_type) is
  begin
     x.i := x.i + 1;    -- just increment for now
  end;

  procedure assign(x: generator'class; new_value: State_Type) is
     procedure doit(x: in out generator) is
     begin
        x.state := new_value;
     end;
     subtype T2 is generator;
  begin
     doit(T2(x));
  end assign;

  function Random (G : Generator) return Float is
     State : State_Type := G.State;
  begin
     newstate(State);
     assign(G, State);
     return float(State.i);     -- just return state for now
  end;
end random_numbers;

with random_numbers;
with text_io;
procedure main is
   g: random_numbers.generator;
begin
   for i in 1..10 loop
     text_io.put_line(float'image(random_numbers.random(g)));
   end loop;
end;

****************************************************************

From: Christophe Grein
Sent: Thursday, January 16, 2003  1:12 AM

>   procedure assign(x: generator'class; new_value: State_Type) is
>      procedure doit(x: in out generator) is
>      begin
>         x.state := new_value;
>      end;
>      subtype T2 is generator;
>   begin
>      doit(T2(x));
               |
     But x is not a variable here, so I do not see why this should work (except
for a bug in Gnat). A view conversion does not change a constant into a
variable.

>   end assign;


I would propose to report this to ACT.

Or am I missing something?

****************************************************************

From: Dan Eilers
Sent: Thursday, January 16, 2003  11:24 AM

Yes, you are right.

A more serious proposal for how to implement Random would be
to define a new pragma that could be applied to private types,
that would mean:
  1) no constants may be declared of this type;
  2) "in" parameters are not implicitly treated as constants
      (either in functions or procedures)

This pragma would be appropriate for type Generator in the Random
packages, and also type File_Type in the I/O packages.

It might also help solve the dilemma caused by AI-287 and AI-318
proposing to allow constants of limited types in the face of the
Rosen trick allowing writing on objects of limited types.   Perhaps
the Rosen trick could be disallowed on limited types that didn't
have this pragma applied, although that would be an incompatibility.

****************************************************************

From the minutes of Meeting 18, February 2003:

Steve Baird points out that the wording of 6.6(3) isn't quite correct. The
instantiation case needs to specify that there are no other parameters.

[If this AI is resurrected someday, that needs to be fixed.]

****************************************************************

From: Pascal Leroy
Sent: Tuesday, January 10, 2006  6:50 AM

[Replying to private mail from Tucker Taft - ED]

...

>      I also am
>      not particularly comfortable with being tagged as the sole
>      holdout on "out" parameters for functions.

But you are ;-)

>      When we discussed
>      this in the past in a more open forum than an ARG meeting,
>      there were many voices in favor of preserving the in-parameter-only
>      rule.  It is conceivable that I am the only ARG member who
>      is still sympathetic with that rule, but I wasn't actually
>      aware of that (not that that changes my view, of course ;-).

I am not sure what forum or what discussion you refer to, but it would be
interesting to hear rational justification from real users. The reason why I am
frustrated by this topic is that it has never been discussed in depth, although
it keeps cropping up from time to time in unrelated discussions (the only time
it was addressed in an ARG meeting was in Padua, and we didn't spend 10 minutes
on it).  My recollection from the Padua meeting is that many people were
concerned about destabilizing the language or compilers.  But it turned out
that over time we became bolder and made other changes that are bigger and more
complex.

****************************************************************

From: Tucker Taft
Sent: Tuesday, January 10, 2006  7:31 AM

...
> I am not sure what forum or what discussion you refer to, but it would be
> interesting to hear rational justification from real users.

I believe it was discussed in depth during the Ada 9X
process.  It may be that was the last time there was
a thorough airing of the issues.  Those heavily involved
in this earlier discussion remember it as being very
contentious, and probably avoided it during the
Ada 200Y process for that reason.

> ... My
> recollection from the Padua meeting is that many people were concerned
> about destabilizing the language or compilers.  But it turned out that
> over time we became bolder and made other changes that are bigger and more
> complex.

It might be worth trying to find the old Ada 9X notes
on this topic some day...  Implementation issues were
only a part of the argument -- the big part had to do
with the likelihood of code being harder to read, understand,
verify, and debug.  Access parameters were the Ada 9X
solution to both kinds of issues, because they allowed
pass-by-reference for "out" parameters (pass-by-copy-out
was felt to be more of a burden in the middle of an
expression), and they made the use highly visible,
due to the need for "aliased" and "'Access".

****************************************************************

From: Pascal Leroy
Sent: Tuesday, January 10, 2006  9:55 AM

I guess it would be better to discuss this topic around a bottle of wine,
however...

> ...the big part had to do
> with the likelihood of code being harder to read, understand,
> verify, and debug.

I have some sympathy with this, but my view is that at the moment we have
the worst of both worlds:

1 - The absence of out parameters for functions is a constant annoyance,
and it makes the language more awkward/less powerful than it should be.  I
don't think that I write particularly disgusting code, but I suspect that
every other day I run into a situation where I wish I could have a
function with an out parameter.  I can invariably find work-arounds, but
they are unnecessarily contrived, and many times they only work because my
program (a compiler) is sequential.

2 - Functions are not "safe" or easy to debug/read/understand.  Basically
the rule is that you can do all the nastiness you wish as long as you
don't document them in the profile.  Side effects, or modifications
through access parameters do not make things particularly easy to
understand, and I would much prefer to see "in out" in the specification
than to find that some function has a side effect because it needs to
updates a cache.

This is why I would love to have two distinct kinds of functions: those
that can do anything, and those that are pure mathematical functions (John
amusingly called them whores and virgins, but I don't think we would want
to adopt this terminology as official technical terms).  That way the
programmer could choose between powerful/flexible and safe/readable, and
of course make the choice on a case-by-case basis.

> Access parameters were the Ada 9X
> solution to both kinds of issues, because they allowed
> pass-by-reference for "out" parameters (pass-by-copy-out was
> felt to be more of a burden in the middle of an expression),
> and they made the use highly visible, due to the need for
> "aliased" and "'Access".

I bought this story 10 years ago but I lost my illusions after trying to
use the damned language.  There is a category of problems (notably in the
OOP world) where access parameters work very well, and this is one of the
reasons why I was all in favor of making them more powerful in Ada 2005.
But if what you are trying to do is have an in out Integer, access
parameters are a big pain in the b*tt.  "Access" and "aliased" stick out
like two sore thumbs, and the readability of the source code is
considerably degraded.  Not to mention that many people probably go for
Unchecked_Access to circumvent the accessibility rules, so they run the
risk of being bitten by dangling pointers.

The bottom line is that you don't help the programmer by making it hard to
solve a problem that pops up all the time.  You help the programmer by
giving them a wide range of tools so that they can choose the most
appropriate for a given problem.

****************************************************************

From: Randy Brukardt
Sent: Tuesday, January 10, 2006  1:12 PM

> I guess it would be better to discuss this topic around a bottle of wine,
> however...

The more bottles, the better. :-) :-)

Tucker said:

> I believe it was discussed in depth during the Ada 9X
> process.  It may be that was the last time there was
> a thorough airing of the issues.  Those heavily involved
> in this earlier discussion remember it as being very
> contentious, and probably avoided it during the
> Ada 200Y process for that reason.

The problem with depending on this is that it is a 15-year old discussion. Much
has changed about Ada, and the world, since then. Moreover, Pascal is proposing
adding a real, checked Pure function, which I think would mitigate some of the
concerns (it would *more* possible to write safe functions than it is now).

Pascal said:

> > ...the big part had to do
> > with the likelihood of code being harder to read, understand,
> > verify, and debug.
>
> I have some sympathy with this, but my view is that at the moment we have
> the worst of both worlds:
>
> 1 - The absence of out parameters for functions is a constant annoyance,
> and it makes the language more awkward/less powerful than it should be.  I
> don't think that I write particularly disgusting code, but I suspect that
> every other day I run into a situation where I wish I could have a
> function with an out parameter.  I can invariably find work-arounds, but
> they are unnecessarily contrived, and many times they only work because my
> program (a compiler) is sequential.
>
> 2 - Functions are not "safe" or easy to debug/read/understand.  Basically
> the rule is that you can do all the nastiness you wish as long as you
> don't document them in the profile.  Side effects, or modifications
> through access parameters do not make things particularly easy to
> understand, and I would much prefer to see "in out" in the specification
> than to find that some function has a side effect because it needs to
> updates a cache.
>
> This is why I would love to have two distinct kinds of functions: those
> that can do anything, and those that are pure mathematical functions (John
> amusingly called them whores and virgins, but I don't think we would want
> to adopt this terminology as official technical terms).  That way the
> programmer could choose between powerful/flexible and safe/readable, and
> of course make the choice on a case-by-case basis.

Exactly.

One middle ground alternative would be to allow "in out" only on by-reference
types (in this case, it is identical to "not null access"). But that seems a
bit goofy, and contract issues would prevent much use in generics.

So I think it is best to eliminate the silly restriction.

> > Access parameters were the Ada 9X
> > solution to both kinds of issues, because they allowed
> > pass-by-reference for "out" parameters (pass-by-copy-out was
> > felt to be more of a burden in the middle of an expression),
> > and they made the use highly visible, due to the need for
> > "aliased" and "'Access".
>
> I bought this story 10 years ago but I lost my illusions after trying to
> use the damned language.  There is a category of problems (notably in the
> OOP world) where access parameters work very well, and this is one of the
> reasons why I was all in favor of making them more powerful in Ada 2005.
> But if what you are trying to do is have an in out Integer, access
> parameters are a big pain in the b*tt.  "Access" and "aliased" stick out
> like two sore thumbs, and the readability of the source code is
> considerably degraded.  Not to mention that many people probably go for
> Unchecked_Access to circumvent the accessibility rules, so they run the
> risk of being bitten by dangling pointers.

Exactly again. Indeed, I was one of the people that was firmly against in out
parameters for functions in Ada 95. But I've found that that it makes a lot of
things harder/uglier. Moreover (unlike Pascal), I try hard to avoid visible
access types in OOP -- they're not usually needed (except for this stupid
case), and cause accessibility issues where there would need to be none. (Claw
has very few visible access types; of course there are a lot of them under the
covers.)

> The bottom line is that you don't help the programmer by making it hard to
> solve a problem that pops up all the time.  You help the programmer by
> giving them a wide range of tools so that they can choose the most
> appropriate for a given problem.

This is one of the best summaries I've heard on the topic. Can I add it to the
AI for future reference?

****************************************************************

From: Tucker Taft
Sent: Saturday, January 21, 2006  7:15 PM

"Out" parameters in functions considered harmful (with apologies to
Dijkstra).

It appears that all arguments opposing "out" parameters in functions
have receded into lost ancient history.  So I thought it might be useful
to try to resurrect some of these arguments, just for the record, so
something can be added to the Appendix of the AI on this topic.  By the
way, the only AI that seems to have any useful discussion of this topic
in its appendix is actually AI-230, rather than AI-323 (the one
officially on function "out" parameters).  Not quite sure how that
happened.  Probably reflects the fact that various ARG members drop
comments about this issue off and on into various random AI-discussions.

So here goes...

The one line "sound bite" rationale for having OUT parameters in
functions in AI-323 is:

   Ada functions can have arbitrary side effects, but are not allowed to
   announce that in their specifications

The key distinction between the side-effects of a function that occur
from code inside the function as opposed to the side-effect of a
function call inherent in an OUT parameter, is that the side effect
inside the function is under control of the function implementor.  The
function implementor uses a side-effect in a function when it is most
appropriate to the purpose of the function.  In this sense, the
side-effect is part of the "abstraction" represented by the function.
For example, a traditional, parameterless random number generator has a
side-effect of updating its internal "seed".  The semantics of the
random number generator are that it returns a different value every time
it is called.  The order of calling is largely irrelevant, since the
whole point is that successive values are meant to be uncorrelated.

Another example is a function that allocates memory.  There is a
side-effect on some storage pool abstraction, but again, the exact order
in which two calls on such a function occur is generally irrelevant,
since one memory location is presumed to be equivalent to some other
memory location, so long as each designates a sufficiently large area.

A third common kind of function with an "internal" side effect is one
that "memoizes" answers.  That is, if you call it with the same
parameters as provided recently, the answer is delivered from some
internal cache, rather than being recomputed.   Again, the exact order
in which two successive calls are processed makes little difference in
the overall result of the program, since the expensive computation is
performed only once, and both calls get the same answer.

Let's contrast this with functions with explicit OUT parameters. Now we
have a case where any expression that involves a call on such a function
where the OUT parameter happens to appear as an IN parameter elsewhere
in the same expression is almost certain to have significant order
dependence.  But there is nothing necessarily visible at the call site
that such an order dependence exists.  In languages like C, a classic
example of this is:

   if (a[i] == b[i++]) { ...

Here there is an evaluation order dependence because "i" appears as an
"out" parameter of the postfix "++" operator as well as an "in"
parameter to the indexing operation.  The natural tendency to read this
left-to-right is misleading, since there is no requirement that the update
implied by the postfix "++" occur after the evaluation of the address
represented by "a[i]". At least in this case the reader can see that the
operator being called is one of those nasty operators with an OUT
parameter, so you can hopefully be warned off your natural left-to-right
evaluation assumptions.

In Ada 201Z, let's presume a programmer writes a post-increment function
using the new cool function OUT parameter capability:

    if A(I) = B(Post_Inc(I)) then ...

Gee, that looks pretty nice, and oh so economical.  Unfortunately, it
now has a nasty evaluation order dependence.

One of the great features of function calls as opposed to procedure
calls is that they can be used in the middle of expressions.  But Ada
makes no attempt to legislate the order of evaluation of expressions.
You can contrast this with Java, where a left-to-right order is
specified.  Personally, I prefer Ada's choice.  Authorizing the
programmer to rely on a strict left-to-right order of evaluation allows
the programmer to play tricks that can make things harder for the reader
to understand.  Interestingly, Java (unlike C++) does not allow
user-defined functions with OUT parameters (in C++, they are called
"ref" parameters, but they are essentially equivalent), though it is C++
that allows arbitrary order of evaluation of expressions. (Java does
still have postfix "++" style operators, so a left-to-right evaluation
order presumably helps there.)

The Ada 95 "solution" to the OUT parameter issue was "access" parameters.
These are admittedly a bit heavy weight, because they require "aliased"
on the variable, and "'Access" at the call point.  But perhaps that is
not so bad.  By marking something as "aliased," we can see that it
might be undergoing updates through multiple paths, and hence evaluation
order dependencies are highly possible.  Similarly, by using 'Access
at the call point, we again make the potential order dependence visible
at the place where they are most likely to occur.  Hence, our Ada 2005
programmer who wants a Post_Inc function will have to write:

    I : aliased Index_Type;

    ...


    if A(I) = B(Post_Inc(I'Access)) then ...

Now hopefully some kind of alarm bell will go off.  At least someone
reviewing this code could do a mechanical search for "'Access" and/or
"aliased" to narrow down the places to look for these kinds of order
dependences.

One argument in favor of OUT parameters over this sort of "access"
parameter approach seems to be one of writer convenience over reader
visibility.  There obviously is a tradeoff here, but clearly Ada's
very heavyweight Unchecked_Conversion approach is an example where
the choice has been made for reader visibility over writer convenience.

There are perhaps other non-convenience arguments in favor of OUT
parameters over "access" parameters, but I'll leave those for others
to provide or resurrect.

****************************************************************

From: Pascal Leroy
Sent: Monday, January 23, 2006  2:32 AM

Thanks for a very useful write-up.  It should definitely be appended to AI
323.

I believe that you lie, though, when you write:

> Hence, our Ada 2005 programmer who wants a Post_Inc function will have
> to write:
>
>    I : aliased Index_Type;
>
>    if A(I) = B(Post_Inc(I'Access)) then ...
>
> Now hopefully some kind of alarm bell will go off.

First, note that in Ada 95 objects allocated in the heap are implicitly
aliased, so if you use an access type you don't have to write 'Access, and
the alarm bell is much harder to hear:

	I : Access_Integer;

	if A (I.all) = B (Post_Inc (I).all) then

Arguably the .all should draw your attention, but things become worse if
both A and B take access parameters:

	if A (I) = B (Post_Inc (I)) then

So it's not like someone looking only at the call site will have a clear
idea that dependences on the order of evaluation exist.

Second, note that in Ada 2005 this Taft person invented the prefixed view,
which gives you an *implicit* 'Access.  So if, instead of looking at a
plain integer, you look at an object of a tagged type, you can write:

	type T is tagged ...;
	function A (Y : T) return ...;
	function B (Y : T) return ...;
	function Post_Inc (Z : access T) return T;

	procedure P (X : T) is
	begin
	   if A (X) = B (X.Post_Inc) then

No alarm bells at all here!  No Access, no aliased.  In fact following
your argument the prefixed view should be consider harmful, because now
anything that looks like a component selection can do arbitrary side
effects:

	if X.Foo = X.Bar then -- possible dependence on evaluation order!

My point is that, despite what you seem to believe, we are already in a
deep pit and we might as well bite the bullet and recognize that arbitrary
side effects could happen without giving a clue to the reader.  In fact,
if we added some form of pure function we would be helping the reader in
many cases, because they could look at the specification of the function
without having to do extensive analysis of the call sites.

****************************************************************

From: Tucker Taft
Sent: Monday, January 23, 2006  7:32 AM

There are always two sides to the "slippery slope" argument.
One is that we might as well slide all the way to the
bottom, the other is that we should dig in our ice axes
and preserve what's left.  OUT parameters will make this
all very easy and common, significantly increasing the
likelihood of unanticipated evaluation order dependences.
Access types, tagged types, and aliased objects already
provide plenty of ways to get side-effects when you need
them.  No need to make it any easier to create evaluation
order dependencies.

****************************************************************

From: Randy Brukardt
Sent: Monday, January 23, 2006  9:54 PM

I think most people would agree that evaluation order dependencies are bad.

But the problem here is that Ada has selected the worst possible solution: it
allows them, does not detect them in any way, and then says that if you depend
on one (even if by accident), your code will fail at some point in the future
when you compiler changes. Yuck.

It would be better to realize that we cannot ban order dependencies, so we
should at least make the code well-defined (which means mandating left-to-right
evaluation). And we should give the programmer tools to avoid them (real pure
functions).

The "unspecified" order in Ada doesn't really make the code better the majority
of the time, and it certainly makes programming in Ada more dangerous than it
has to be. I'd figure that the odds that I have order dependencies in my code
is probably nearly 100% (pretty much like erroneous code).

Effectively, you're using two bugs in the language (unspecified order of
evaluation and no way to ensure that functions don't have side-effects) to
justify a third bug (out parameters for function). Three wrongs don't make a
right, or even less wrong!!!

****************************************************************

From: John Barnes
Sent: Tuesday, January 24, 2006  2:22 AM

> The "unspecified" order in Ada doesn't really make the code better the
> majority of the time, and it certainly makes programming in Ada more
> dangerous than it has to be. I'd figure that the odds that I have order
> dependencies in my code is probably nearly 100% (pretty much like
> erroneous code).

The one thing I regretted about RTL/2 which I devised some 35 years ago was
that I did not specify order of evaluations. This was to help with
optimization. It was I believe the only serious risk of non-portability.

> Effectively, you're using two bugs in the language (unspecified order of
> evaluation and no way to ensure that functions don't have side-effects) to
> justify a third bug (out parameters for function). Three wrongs don't make
> a right, or even less wrong!!!

Bob Duff just showed me a trick attributed to JPR for doing things like
random number generators. I am shocked that the absence of in out parameters
has driven people to do that sort of thing.

****************************************************************

From: Tucker Taft
Sent: Tuesday, January 24, 2006  7:03 AM

We seem to have drifted a bit.  As far
as unspecified order of evaluation, I am
more on Pascal's side here.  I was inspired
by Dijkstra's "Discipline of Programming"
I suspect.  His mini-language uses
non-determinacy heavily, where every "if"
and "while" statement non-deterministically
chooses among alternatives that are allowed
to overlap.  E.g., for "max":

     if
       X >= Y  =>  return X;
       Y >= X  =>  return Y;
     fi

Dijkstra's claim is that removing determinancy
rules actually make program proofs simpler.

Furthermore if any programmer takes advantage
of determinancy rules about order of
evaluation, etc., is creating a program that
may be harder to understand in general.

I guess my preference would be the SPARK approach,
where order of evaluation is unspecified but
the language rules try to statically eliminate
cases where order of evaluation matters.

Pascal's suggestion of disallowing a given
object to be used more than once in a single
expression if any of the uses are [IN] OUT
parameters is an example.  Unfortunately,
such rules can be complex, and may be more
appropriate for a "full" static analyzer
(like our "Inspector" product, or the SPARK
Examiner).

****************************************************************

From: Pascal Leroy
Sent: Tuesday, January 24, 2006  7:35 AM

>      if
>        X >= Y  =>  return X;
>        Y >= X  =>  return Y;
>      fi
>
> Dijkstra's claim is that removing determinancy
> rules actually make program proofs simpler.

I am not familiar with Dijkstra's language, but I have used Prolog quite a
bit, and that's a fully nondeterministic language.  As in your example
above, you would just describe all the possible alternatives, and it would
pick one at execution.  The beautiful thing was that you didn't have to
break the symmetry of the problem at hand like you do constantly in
procedural languages (e.g. with the if statement).  The drawback of course
was that any real-life algorithm ended up exponential, but that's a
detail.

> Furthermore if any programmer takes advantage
> of determinancy rules about order of
> evaluation, etc., is creating a program that
> may be harder to understand in general.

Absolutely.

> Pascal's suggestion of disallowing a given
> object to be used more than once in a single
> expression if any of the uses are [IN] OUT
> parameters is an example.  Unfortunately,
> such rules can be complex...

True, but it all depends on how far you want to go.  Remember, if you make
the rules too simple, the worst that can happen is that the user has to
introduce a temporary.  My gut feeling is that you would want to use type
information, aliasedness, and probably not much more (in particular, no
data flow analysis).  We are probably talking a complexity similar to that
of the accessibility rules.  No big deal ;-)

****************************************************************

From: Randy Brukardt
Sent: Tuesday, January 24, 2006  1:48 PM

> > Pascal's suggestion of disallowing a given
> > object to be used more than once in a single
> > expression if any of the uses are [IN] OUT
> > parameters is an example.  Unfortunately,
> > such rules can be complex...
>
> True, but it all depends on how far you want to go.  Remember, if you make
> the rules too simple, the worst that can happen is that the user has to
> introduce a temporary.  My gut feeling is that you would want to use type
> information, aliasedness, and probably not much more (in particular, no
> data flow analysis).  We are probably talking a complexity similar to that
> of the accessibility rules.  No big deal ;-)

I think this would be a reasonable compromise, presuming the rules could be
worked out. It would seem that such a check would only need to apply to
parameters that aren't by-reference (although one could make an argument to
include them). But it would be good to use the same rules for procedure calls,
and there you wouldn't want to ban by-reference parameters that are the same.
(Note that you may not be able to have a temporary of a by-reference type.)

In any case, my point is that if you are worried about side-effects in
expressions, let's attack *that* problem, not eliminate the possibility of
caching results in objects, writing sane random number generators, writing
interfacing code that maps the meaning closely, and the like.

'In Out' parameters in functions are *not* a problem. Order-dependent
expressions are a problem! Banning the former does little to help the latter,
and it makes a lot of things harder than they need to be.

****************************************************************

From: Pascal Leroy
Sent: Wednesday, January 25, 2006  3:17 AM

> (Note that you may
> not be able to have a temporary of a by-reference type.)

True, and the same holds for limited types.  But then you could probably
use renamings.  Remember, the only thing you have to do is to break
complex expressions into individual declarations or statements in order to
spell out the evaluation order.

It would be an interesting exercise to try to spell out the rules and see
where this leads us.  But not today...

> 'In Out' parameters in functions are *not* a problem.
> Order-dependent expressions are a problem! Banning the former
> does little to help the latter, and it makes a lot of things
> harder than they need to be.

I couldn't agree more.

****************************************************************

From: Robert Dewar
Sent: Thursday, September 28, 2006  11:20 PM

> I'm not sure the analogy is very good, since this was just
> a simple yes or no (although there were some creative
> middle grounds that were suggested at some point in
> the past year that might be worth revisiting, FWIW).

(off topic I know, but I didn't start it :-))

These middle grounds would be interesting to look at, if they
resulted in any movement on this issue. I continue to find
the prohibition of out parameters in functions to be one
of the silly and annoying design decisions in Ada. Every
time I run into it, I just have to rig up some more or
less ugly circumvention that lets me do what I want.

****************************************************************

From: Tucker Taft
Sent: Friday, September 29, 2006  12:11 AM

For what it is worth, here is a quick summary of Pascal's
suggestion, and his further comment on it (both can
be found in the Appendix of AI95-00323).

[Followed by mail of January 24, 2006, found above.]

****************************************************************


Questions? Ask the ACAA Technical Agent