Version 1.1 of acs/ac-00176.txt

Unformatted version of acs/ac-00176.txt version 1.1
Other versions for file acs/ac-00176.txt

!standard 5.10          09-06-02 AC95-00176/01
!class Amendment 09-06-02
!status received no action 09-06-02
!status received 09-03-09
!subject Calling anonymous subprograms
!summary
!appendix

From: Randy Brukardt
Date: Monday, March 9, 2009 11:41 PM

> ...
> > A second idea is to have syntactic sugar for passing subprograms as
parameters.
> > If we make it sweet enough, Update_Element calls could be made readable.
> > We'd have to allow the actual procedure to be anonymous, as in Lisp
> > lambdas, and get rid of most boilerplate verbiage.
>
> You've mentioned this before, but I don't see how it could work. If
> you require writing enough stuff so that the result makes sense to a
> casual reader, it's still going to be a big glob of text (you can't
> get rid of the parameter decls or the begin and end). Sticking that in
> the middle of a call doesn't sound pleasant. I suppose you could make
> the parameters implicit, but then readability is going to be severely
> impacted, because you'd have objects that are referenced by never
> declared anywhere in the text (at least nowhere obvious). That doesn't
> sound very Ada-like.

Of course, since Bob planted this seed, I've been thinking about whether it
makes sense to look at this approach any further. It's probably the flu I have
(making me delirious), but I think I've got it. (Whether it is a better idea
than the one given in AI05-0142-1 remains to be seen.)

Behold the For_Do_Statement (a better name is needed!!)

for_do_statement ::= [FD_identifier:] for Call_Name do procedure formal_part_option is
                        declarative_part
                     begin
                        handled_sequence_of_statements
                     end FD_identifer;

Call_Name must resolve to a procedure call with the last parameter omitted.
(This could be in prefix notation or in traditional notation.) Rules similar to
those for a prefix call would be used.

Legality rules: The last parameter of the profile of the called subprogram shall
have an anonymous access-to-procedure. The formal_part_option in the
for_do_statement shall subtype conform to the profile of the anonymous
access-to-procedure (we don't care about the defaults in the formal_part_option,
they could never be used, thus we only need subtype conformance). Gotos and
exits from the handled_sequence_of_statements would be prohibited. (Probably
could be made to work, but since there is a procedure call under the covers
here, the work would be substantial. Not clear that it would be worth it.)

Dynamic semantics: The "procedure" given here is passed as the (missing) actual
parameter to the call Call_Name. (Obviously, the compiler would need to make it
callable as needed.)

For the predefined containers, this could be used like:

    for My_Vector.Update_Element (Position => 10) do procedure (Element : in out My_Element_Type) is
    begin
       if Element = Wow then
           Element := Whee;
       end if;
    end;

BTW, I actually checked this through our grammar generator, and it works fine as
long as it is classified as a loop for the purposes of our compiler's syntax.
Which I don't think would be a problem (just a bit weird). The identifier at the
start is needed for that to work, so that is a requirement of this syntax
(starting with "For"). I had tried syntaxes starting with Do without much luck
getting it to read well.

It should noted that parts of this syntax isn't really needed. For instance, the
parameter list could be implied from the profile of the implicit
access-to-subprogram parameter. I'm dubious that is a good idea, though.

While this doesn't save a whole lot of typing, at least it puts the body of the
"procedure" where it needs to be, making the text more readable. Note that the
syntax is pretty complex because it allows the full generality of a procedure,
but in practice you'd only use the simplest parts (as shown in the example).

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

From: Bob Duff
Date: Tuesday, March 10, 2009   9:05 AM

> Of course, since Bob planted this seed, I've been thinking about
> whether it makes sense to look at this approach any further. It's
> probably the flu I have (making me delirious), but I think I've got
> it. (Whether it is a better idea than the one given in AI05-0142-1
> remains to be seen.)

I don't have time to fully evaluate this right now, but my first impression is
that I like it.

> For the predefined containers, this could be used like:
>
>     for My_Vector.Update_Element (Position => 10) do procedure
> (Element : in out My_Element_Type) is
>     begin
>        if Element = Wow then
>            Element := Whee;
>        end if;
>     end;
>
> BTW, I actually checked this through our grammar generator, and it
> works fine as long as it is classified as a loop for the purposes of
> our compiler's syntax. Which I don't think would be a problem (just a
> bit weird). The identifier at the start is needed for that to work, so
> that is a requirement of this syntax (starting with "For"). I had
> tried syntaxes starting with Do without much luck getting it to read well.

"do"..."with procedure" could work as well as "for"..."do procedure".

Or maybe something like:

    for My_Vector.Update_Element (Position => 10) with procedure (Element : in out My_Element_Type) is
    ... -- as above

or:

    do My_Vector.Update_Element (Position => 10) with Process => (Element : in out My_Element_Type) is
    ... -- as above

Just thinking aloud, here...

> It should noted that parts of this syntax isn't really needed. For
> instance, the parameter list could be implied from the profile of the
> implicit access-to-subprogram parameter. I'm dubious that is a good idea, though.

At the very least, the parameter names are essential, IMHO. If you didn't
declare "Element" above, then the visibility would be confusing, especially if
there's something else called "Element" around the place.

Parameter names can't be overloaded, so we could leave out the type.
I'm not sure that's a good idea, though.

Leaving out the mode is also questionable (unless of course it's "in").

> While this doesn't save a whole lot of typing, at least it puts the
> body of the "procedure" where it needs to be, making the text more readable.

Right.  The goal isn't so much to "save a whole lot of typing".
The goal, as you say, is to put the body where it belongs.
And the other goal is to make it anonymous, so you don't have to clutter the
program with meaningless names.

>...Note
> that the syntax is pretty complex because it allows the full
>generality of a  procedure, but in practice you'd only use the simplest
>parts (as shown in  the example).

P.S. Sorry you've got the flu.  That's no fun.  Get better soon!

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

From: Randy Brukardt
Date: Tuesday, March 10, 2009  2:19 PM

...
> "do"..."with procedure" could work as well as "for"..."do procedure".

Actually, that was my original idea. In my flu-induced fog last night, I thought
it didn't read well, but looking at it now, I think I prefer it to the original
proposal.

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

From: Adam Beneschan
Date: Tuesday, March 10, 2009  2:02 PM

A couple comments just about the particular syntax:

(1) It's fortuitous that all of the Ada.Containers routines that take
    an access-procedure parameter take it as the last parameter.  Or
    maybe this was done deliberately in anticipation of Randy making a
    proposal like this...?  Anyway, a possible way to relax this
    restriction would be to allow a syntax something like this:

     for My_Vector.Update_Element (Position => 10, Process => <>) do procedure
                                       (Element : in out My_Element_Type) is

    where you could only have one actual parameter that is <>, and it
    would have to correspond to an access-procedure formal parameter,
    but it wouldn't have to be the last in the profile.  I'm
    suggesting this as an addition to Randy's syntax, not as an
    "instead of"---i.e. both could be allowed.

(2) All of the other compound statements except "block_statement"
    require an additional word after the "end": end loop, end if, end
    case, end select; and the "end" of an accept_statement must be
    followed by the entry name.  For consistency, I think the same
    should be the case here (probably "end do
    [optional-identifier];").

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

From: Micronian
Date: Tuesday, March 10, 2009  3:06 PM

Yes, let's not cripple the feature by requiring that a parameter order has to be
followed. In addition, something like '<>' actually makes it much clearer from a
reader's point where the anonymous procedure is intended to be used.

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

From: Bill Findlay
Date: Tuesday, March 10, 2009  2:16 PM

for My_Vector.Update_Element (Position => 10)'Process
   use procedure (Element : in out My_Element_Type) is
       begin
          if Element = Wow then
             Element := Whee;
          end if;
       end;
?

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

From: Jeffrey R. Carter
Date: Tuesday, March 10, 2009  1:55 PM

> "do"..."with procedure" could work as well as "for"..."do procedure".

Or we could use "for ... use" like attribute definitions. The possibilities are
endless.

What about functions?

But is this really worth the effort? If the object is to have the subprogram
body at the point of the call, isn't

declare
    procedure Temp (Element : in out E) is
       ...
    begin -- Temp
       ...
    end Temp;
begin
    V.Update_Element (Position => 10, Process => Temp'access); end;

sufficient?

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

From: Niklas Holsti
Date: Tuesday, March 10, 2009  3:09 PM

[ proposed syntax of a "for" statement that supplies an anonymous "in-line" actual subprogram for the final "access subprogram"
parameter of a call ]

>>For the predefined containers, this could be used like:
>>
>>    for My_Vector.Update_Element (Position => 10) do
>>    procedure (Element : in out My_Element_Type) is
>>    begin
>>       if Element = Wow then
>>           Element := Whee;
>>       end if;
>>    end;

This strikes me as a very special-purpose syntactical sugar for Update_Element
and its relatives. As Randy explained, it is limited to one access-subprogram
parameter, which must be the positionally last actual parameter. This may
interfere with profiles in which the access-subprogram parameter is followed by
parameters with useful default values, meant to be omitted in (positionally
associated) calls.

I think a normal parameter association, extended with an "in line" subprogram
syntax, would be better, and would not be limited to a single, final
access-subprogram parameter. In the example, this would be:

    My_Vector.Update_Element (
       Position => 10,
       Process  => procedure (Element : in out My_Element_Type) is
          begin
             if Element = Wow then
                Element => Whee;
             end if;
          end Process);

(The "end" of the actual in-line procedure here repeats the name of the formal
parameter, "Process".)

>>BTW, I actually checked this through our grammar generator,  and it
>>works fine ...

In this case, I think the syntax and functionality of the suggested "for"
statement are too poor to be accepted just because it works in Randy's parser.

I realize that my suggestion may be the "general lambda expression" that I think
was rejected earlier in this thread, but the difference in these two forms seems
almost entirely syntactical, so I don't understand the deep reasons, if any, for
that rejection.

>>It should noted that parts of this syntax isn't really needed.
>>For instance, the parameter list could be implied from the profile of
>>the implicit access-to-subprogram parameter. I'm dubious that  is a
>>good idea, though.

Isn't it possible to have overloaded subprograms (Update_Element in the example)
in which the profile of the access-subprogram parameter is needed to resolve the
overloading in the call? If so, the parameter list is necessary in general.

If a programmer wants to skimp on writing, and is ready to risk overload
resolution problems, I suggest to allow the elision of the parameter list by <>:

    My_Vector.Update_Element (
       Position => 10,
       Process  => procedure (<>) is
          begin
             if Element = Wow then
                Element := Whee;
             end if;
          end Process);

> At the very least, the parameter names are essential, IMHO.
> If you didn't declare "Element" above, then the visibility would be
> confusing, especially if there's something else called "Element"
> around the place.

I agree that confusion is possible if the parameter names are elided, but I
would leave this as a decision for the programmer (or the applicable coding
rules).

A compiler could help by warning if the elided parameter names hide some
higher-level names. (Warnings for name hiding would be useful in other areas of
Ada, too.)

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

From: Bob Duff
Date: Tuesday, March 10, 2009  3:23 PM

>     My_Vector.Update_Element (
>        Position => 10,
>        Process  => procedure (Element : in out My_Element_Type) is
>           begin
>              if Element = Wow then
>                 Element := Whee;
>              end if;
>           end Process);

Not bad.

If I have a tree-walker that takes a Pre_Action and a Post_Action, it would be
nice to specify both anonymously.  It would also be nice to have things like
Lisp's mapcar, that take a function to apply to every element of a list, and
pass that function anonymously.

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

From: Bob Duff
Date: Tuesday, March 10, 2009  3:55 PM

> Or we could use "for ... use" like attribute definitions. The
> possibilities are endless.

I don't much like for...use, because this beast is nothing like an attribute_definition_clause.

> What about functions?

Good question.  A really good solution would support them, as well.

> But is this really worth the effort? If the object is to have the
> subprogram body at the point of the call, isn't
>
> declare
>     procedure Temp (Element : in out E) is
>        ...
>     begin -- Temp
>        ...
>     end Temp;
> begin
>     V.Update_Element (Position => 10, Process => Temp'access); end;
>
> sufficient?

Well, I suppose that's a matter of taste.

The above is what we have now, and as far as run-time semantics, it works just
fine.  But it contains rather a lot of noise, when the body of Temp is just 2 or
3 lines of useful code.  And Temp is in the conceptually "wrong" place. And in
real-life examples, it's usually impossible to come up with a good name for Temp
(which is a symptom of the fact that it's not a real abstraction on its own).
And using a name at all adds complexity to the program -- the reader must match
up the reference to Temp to the declaration of Temp.

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

From: Jeffrey R. Carter
Date: Tuesday, March 10, 2009  6:43 PM

> The above is what we have now, and as far as run-time semantics, it
> works just fine.  But it contains rather a lot of noise, when the body
> of Temp is just 2 or 3 lines of useful code.  And Temp is in the conceptually "wrong" place.
> And in real-life examples, it's usually impossible to come up with a
> good name for Temp (which is a symptom of the fact that it's not a
> real abstraction on its own).  And using a name at all adds complexity
> to the program -- the reader must match up the reference to Temp to
> the declaration of Temp.

I don't see it containing "rather a lot of noise". Compared to the proposals
given here, it has "declare", "begin", "end", and "Temp" as the only additional
text.

I don't usually find it hard to come up with good names for such subprograms. In
the original example, the name would be Increment. And if you feel that such
subprograms don't have or deserve good names, then using the formal parameter
name avoids any effort and reduces any complexity in matching the reference to
the subprogram to its declaration.

As to the conceptually "wrong" place, we have always expected subprograms to be
in a declarative region prior to referencing them. I don't see why we would have
a different concept in this case.

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

From: Dmitry A. Kazakov
Date: Wednesday, March 11, 2009  3:48 AM

>     My_Vector.Update_Element (
>        Position => 10,
>        Process  => procedure (Element : in out My_Element_Type) is
>           begin
>              if Element = Wow then
>                 Element := Whee;
>              end if;
>           end Process);

   procedure (Element : in out My_Element_Type) is ... end Process

is obviously not a pointer to a procedure but a literal of.

Why not to be honest, and not to make Update_Element:

   procedure Update_Element (
      Container : in out Vector;
      Position : Integer;
      Process : procedure (Element : in out My_Element_Type));
           ^^^^^^ no ugly "access" here

then the literal procedure ... end; would fit.

> (The "end" of the actual in-line procedure here repeats the name of
> the formal parameter, "Process".)

What about moving formals to the key, dropping "procedure" and using "do"
instead of "begin":

   My_Vector.Update_Element (
      Position => 10,
      Process (Element : in out My_Element_Type)  =>
         do
            if Element = Wow then
               Element := Whee;
            end if;
         end Process);

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

From: NiKlas Holsti
Date: Wednesday, March 11, 2009  4:03 PM

>    procedure (Element : in out My_Element_Type) is ... end Process
>
> is obviously not a pointer to a procedure but a literal of.

Put an "access" in front if you don't like to imagine an implicit one.

> Why not to be honest, and not to make Update_Element:
>
>    procedure Update_Element (
>       Container : in out Vector;
>       Position : Integer;
>       Process : procedure (Element : in out My_Element_Type));
>            ^^^^^^ no ugly "access" here
>
> then the literal procedure ... end; would fit.

OK by me. I think the omission of the "access" keyword here, and in the call
above, is harmless and similar to the omission of ".all" when referring to a
record component via an access to the record. (Although the latter omission is
much more important and is one of the nicest syntactical sweeteners in Ada, I
think.)

> What about moving formals to the key, dropping "procedure" and using "do"
> instead of "begin":
>
>    My_Vector.Update_Element (
>       Position => 10,
>       Process (Element : in out My_Element_Type)  =>
>          do
>             if Element = Wow then
>                Element := Whee;
>             end if;
>          end Process);

That would distort the named-association syntax too much, in my opinion. The
"=>" could end up several lines away from the formal parameter name.

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

From: Jean-Pierre Rosen
Date: Wednesday, March 11, 2009  4:00 AM

> Why not to be honest, and not to make Update_Element:
>
>    procedure Update_Element (
>       Container : in out Vector;
>       Position : Integer;
>       Process : procedure (Element : in out My_Element_Type));
>            ^^^^^^ no ugly "access" here
>
> then the literal procedure ... end; would fit.

Yes, but sometimes you do want to pass a named procedure

> What about moving formals to the key, dropping "procedure" and using "do"
> instead of "begin":
>
>    My_Vector.Update_Element (
>       Position => 10,
>       Process (Element : in out My_Element_Type)  =>
>          do
>             if Element = Wow then
>                Element := Whee;
>             end if;
>          end Process);

How do you declare local variables?

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

From: Bill Findlay
Date: Wednesday, March 11, 2009  7:22 AM

>> The above is what we have now, and as far as run-time semantics, it
>> works just fine.  But it contains rather a lot of noise, when the
>> body of Temp is just 2 or 3 lines of useful code.  And Temp is in the
>> conceptually "wrong" place.
>> And in real-life examples, it's usually impossible to come up with a
>> good name for Temp (which is a symptom of the fact that it's not a
>> real abstraction on its own).  And using a name at all adds
>> complexity to the program -- the reader must match up the reference
>> to Temp to the declaration of Temp.
>
> I don't see it containing "rather a lot of noise". Compared to the
> proposals given here, it has "declare", "begin", "end", and "Temp" as
> the only additional text.

Despite my earlier intervention, I must say I agree with this wholeheartedly.

> I don't usually find it hard to come up with good names for such subprograms.
> In
> the original example, the name would be Increment. And if you feel
> that such subprograms don't have or deserve good names, then using the
> formal parameter name avoids any effort and reduces any complexity in
> matching the reference to the subprogram to its declaration.

More, encouraging the programmer not to give a name to the procedure seems to me
to be contrary to the Ada way.

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

From: Alejandro R. Mosteo
Date: Wednesday, March 11, 2009  6:54 AM

The above is what we have now, and as far as run-time semantics, it works just
fine.  But it contains rather a lot of noise, when the body of Temp is just 2
or 3 lines of useful code.  And Temp is in the conceptually "wrong" place.
And in real-life examples, it's usually impossible to come up with a good
name for Temp (which is a symptom of the fact that it's not a real
abstraction on its own).  And using a name at all adds complexity to the
program -- the reader must match up the reference to Temp to the declaration of
Temp.

Not a language lawyer here, but I wanted to say that my experience completely
matches this comment; both about the noise, naming difficulties and code
placement. The latter is even more painful when you want to iterate over a
container to update in place, since you need two auxiliary procedures, both
placed in reverse order before the actual call. Methinks this is too much apart
from the rest of the imperative traits of Ada to be comfortable.

Of course, the standard containers could also provide an extra iterate procedure
in which instead of taking a procedure with a cursor they already take an in out
element as parameter.

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

From: Bob Duff
Date: Wednesday, March 11, 2009  5:55 PM

> > Why not to be honest, and not to make Update_Element:
> >
> >    procedure Update_Element (
> >       Container : in out Vector;
> >       Position : Integer;
> >       Process : procedure (Element : in out My_Element_Type));
> >            ^^^^^^ no ugly "access" here
> >
> > then the literal procedure ... end; would fit.
> Yes, but sometimes you do want to pass a named procedure

Yes, sometimes.  So how about we have a shorthand notation: For a formal
parameter, "X: procedure..." is syntactic sugar for "X: not null access
procedure...". (Similar for "function".) And for an actual subprogram parameter,
"Blah" is sugar for "Blah'Access".

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

From: Bob Duff
Date: Wednesday, March 11, 2009  6:12 PM

> > I don't see it containing "rather a lot of noise". Compared to the
> > proposals given here, it has "declare", "begin", "end", and "Temp"
> > as the only additional text.
>
> Despite my earlier intervention, I must say I agree with this
> wholeheartedly.

Compared to 2 or 3 lines of useful code, that "declare", "begin", "end", and
"Temp" is a lot, given that it provides zero useful information to the reader of
the code.  The other proposals in this thread have some verbosity, but at least
they're providing useful information (parameter names and types, for example).

> > I don't usually find it hard to come up with good names for such subprograms.
> > In > > the original example, the name would be Increment. And if you feel
> > that such subprograms don't have or deserve good names, then using
> > the formal parameter name avoids any effort and reduces any
> > complexity in matching the reference to the subprogram to its declaration.
>
> More, encouraging the programmer not to give a name to the procedure
> seems to me to be contrary to the Ada way.

I disagree, unless you think "the Ada way" is to require useless verbiage.

Example: suppose we have an array A, and we want to do "A(I) := A(I) + 1;".
I don't think it's the "Ada way" to require a name for that, as in:

    procedure Increment_A_Sub_I is
    begin
        A(I) := A(I) + 1;
    end Increment_A_Sub_I;

But that's exactly what is currently required, if A is a Vector as opposed to a
plain old array.  Or to be even sillier:

    procedure Increment_A_Sub_I is
        function A_Sub_I_Incremented return My_Integer is
        begin
            return A(I) + 1;
        end A_Sub_I_Incremented;
    begin
        A(I) := A_Sub_I_Incremented;
    end Increment_A_Sub_I;

My point is: not everything needs a name.  I'm happy to come up with good names
for good abstractions.  But please don't require every little thing to be a
named abstraction.

Note that "A(I) := A(I) + 1;" might be inside a loop or 'if', and THAT's where I
might want to have a useful name, like Increment_All, or Increment_If_Necessary
or whatever.  Inside usefully-named abstractions, additional names are sometimes
just noise.

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

From: Randy Brukardt
Date: Wednesday, March 11, 2009  7:11 PM

> Compared to 2 or 3 lines of useful code, that "declare", "begin",
> "end", and "Temp" is a lot, given that it provides zero useful
> information to the reader of the code.  The other proposals in this
> thread have some verbosity, but at least they're providing useful
> information (parameter names and types, for example).

I agree. But actually, I think these proposals (including the one I started
with) really are much too wordy for the problem.

...
>Example: suppose we have an array A, and we want to do "A(I) := A(I) + 1;".
>I don't think it's the "Ada way" to require a name for that, as in:
>
>    procedure Increment_A_Sub_I is
>    begin
>        A(I) := A(I) + 1;
>    end Increment_A_Sub_I;
>
>But that's exactly what is currently required, if A is a Vector as
>opposed
to a plain old array.

Right. But let's look at what is required now to do A(I) := A(I) + 1 for a
vector:

   declare
       procedure Temp (Element : in out E) is
       begin
          Element := Element + 1;
       end Temp;
   begin
       A.Update_Element (Position => I, Process => Temp'access);
   end;

IMHO, this is completely ridiculous. Now let's look at some of the
proposals:

   do A.Update_Element (Position => I) with procedure (Element : in out E) is
       begin
          Element := Element + 1;
       end;

Definitely better, but still not great. Or:

   A.Update_Element (Position => I,
      Process => procedure (Element : in out E) is
       begin
          Element := Element + 1;
       end);

Still not that great.

It's even worst for the list container. If you declared the list yourself using
an access type, you would write:

   C.all := C.all + 1;

(C is the "cursor" here.)

   List.Update_Element (Position => C,
      Process => procedure (Element : in out E) is
       begin
          Element := Element + 1;
       end);

seems horribly wordy in contrast. And still seems confusing because there is not
a strong connection between the element and the addition. It would be better to
be able to say something like:

    List.Element_Access (C).all := List.Element_Access (C).all + 1;

You wouldn't actually need to use the accessor on the right-hand-side, so you
could say:

    List.Element_Access (C).all := List.Element (C) + 1;

(or even better than either of these, remembering an old comp.lang.ada
thread:

    List.Element_Access (C).all := <> + 1;

where <> means repeat the target of the assignment.)

Anyway, my point is that decreasing the wordiness here is critical; every extra
noise word adds to the likelihood that people will not use the predefined
containers and thus run aground on the shores of manual storage management and
general reinvention of the wheel.

I don't care in the least about any other case other than the containers - I
surely would not bother to consider any of these ideas without the difficulty of
updating container elements. So whatever happens in other cases isn't that
important to me; other uses is a bonus, not a requirement. (That's why I'm not
very interested in generalizations if they require more words in the
Update_Element cases.)

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

From: Jeffrey R. Carter
Date: Wednesday, March 11, 2009  7:10 PM

> Yes, sometimes.  So how about we have a shorthand notation:
> For a formal parameter, "X: procedure..." is syntactic sugar for "X:
> not null access procedure...". (Similar for "function".) And for an
> actual subprogram parameter, "Blah" is sugar for "Blah'Access".

Except that in that case, you could not call function Blah which returns an
appropriate subprogram access value.

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

From: Jeffery R. Carter
Date: Wednesday, March 11, 2009  7:49 PM

>
>    declare
>        procedure Temp (Element : in out E) is
>        begin
>           Element := Element + 1;
>        end Temp;
>    begin
>        A.Update_Element (Position => I, Process => Temp'access);
>    end;

Or

A.Replace_Element (Index => I, New_Item => A.Element (I) + 1);

>    List.Update_Element (Position => C,
>       Process => procedure (Element : in out E) is
>        begin
>           Element := Element + 1;
>        end);

Or

List.Replace_Element (Position => C, New_Item => List.Element (C) + 1);

We probably need a better example than incrementing.

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

From: Randy Brukardt
Date: Wednesday, March 11, 2009  9:24 PM

...
>Or
>
>List.Replace_Element (Position => C, New_Item => List.Element
>(C) + 1);
>
>We probably need a better example than incrementing.

Yes, of course. One obvious way to look at it is a large record type with a
component Comp which we need to increment. Then the overhead of copying it would
be significant. (A container of containers (like a list of vectors) is a better
example, but that gets a bit too confusing. Anyway, assuming that copying the
element is a bad idea for whatever reason, you can write:

   C.Comp := C.Comp + 1;

for a list written using explicit access types, but now have to write:

    declare
        procedure Temp (Element : in out E) is
        begin
           Element.Comp := Element.Comp + 1;
        end Temp;
    begin
       A.Update_Element (Position => C, Process => Temp'access);
    end;

And the proposals would look like:

    List.Update_Element (C,
       Process => procedure (Element : in out E) is
       begin
          Element.Comp := Element.Comp + 1;
       end);

or

    do List.Update_Element (C) with procedure (Element : in out E) is
       begin
          Element.Comp := Element.Comp + 1;
       end);

Definitely better, but still a ways from ideal.

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

From: Bill Findlay
Date: Wednesday, March 11, 2009  10:05 PM

>> More, encouraging the programmer not to give a name to the procedure
>> seems to me to be contrary to the Ada way.
>
> I disagree, unless you think "the Ada way" is to require useless verbiage.
>
> Example: suppose we have an array A, and we want to do "A(I) := A(I) + 1;".
> I don't think it's the "Ada way" to require a name for that, as in:
>
>     procedure Increment_A_Sub_I is
>     begin
>         A(I) := A(I) + 1;
>     end Increment_A_Sub_I;

It may be verbiage, but it's not useless.
Without an identifier the ONLY way a reader can glean the intention of to read
the internal logic of the subprogram parameter in detail. Unless, of course,
there is a comment that does just what the identifier would do.

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

From: Niklas Holsti
Date: Thursday, March 12, 2009  2:12 AM

> Now let's look at some of the proposals:
>
>  ...
>
>    A.Update_Element (Position => I,
>       Process => procedure (Element : in out E) is
>        begin
>           Element := Element + 1;
>        end);
>
> Still not that great.

The most compact form of those so far proposed would be:

    A.Update_Element (
       I,
       procedure (<>) is begin Element := Element + 1; end);

The initial part of the "procedure literal", up to the "begin", can clearly be
implicit, giving the one-liner:

    A.Update_Element (I, begin Element := Element + 1; end);

This very compressed form of course hides (by making implicit) a lot of
information that is present in the corresponding code in current Ada (with a
named procedure, 'Access etc.). For large access-subprogram parameters this
hiding could be confusing. However, I think this thread is about reducing the
"verbiage" overhead for simple cases as above. The complex cases I would still
write with named procedures in the current way.

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

From: Niklas Holsti
Date: Thursday, March 12, 2009  2:19 AM

> It may be verbiage, but it's not useless.
> Without an identifier the ONLY way a reader can glean the intention of
> to read the internal logic of the subprogram parameter in detail.

By this argument, the names for such tiny subprograms could be as useless as the
justly reviled redundant comments, for example:

    -- Increment A(I):
    A(I) := A(I) + 1;

 > Unless, of course, there is a comment that does just what the  > identifier would do.

Such comments can be put on the call to the subprogram that has the
access-subprogram parameter:

    -- Increment the number of woogles for this parfer:
    A.Update_Element (I, begin Element := Element + 1; end);

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

From: Dmitry A. Kazakov
Date: Thursday, March 12, 2009  3:33 AM

>> Why not to be honest, and not to make Update_Element:
>>
>>    procedure Update_Element (
>>       Container : in out Vector;
>>       Position : Integer;
>>       Process : procedure (Element : in out My_Element_Type));
>>            ^^^^^^ no ugly "access" here
>>
>> then the literal procedure ... end; would fit.

> Yes, but sometimes you do want to pass a named procedure

No problem:

   A.Update_Element (1, My_Procedure);

My_Procedure has the type "procedure (Element : in out My_Element_Type)".

Further the following definitions are equivalent:

   procedure My_Procedure (Element : in out My_Element_Type) is
      ...
   begin
       ..
   end My_Procedure;

and the same in the form: <name> : <type> := <value>:

   My_Procedure : constant procedure (Element : in out My_Element_Type) :=
       procedure (Element : in out My_Element_Type) is
          ...
       begin
          ...
       end My_Procedure;

Here <value> is a procedural literal as in Update_Element call above.

>> What about moving formals to the key, dropping "procedure" and using "do"
>> instead of "begin":
>>
>>    My_Vector.Update_Element (
>>       Position => 10,
>>       Process (Element : in out My_Element_Type)  =>
>>          do
>>             if Element = Wow then
>>                Element => Whee;
>>             end if;
>>          end Process);
>>
> How do you declare local variables?

The same way we do in the accept-statement which I used as a template.

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

From: Jean-Pierre Rosen
Date: Thursday, March 12, 2009  3:51 AM

>> How do you declare local variables?
>
> The same way we do in the accept-statement which I used as a template.

i.e, use a nested block. Not very pretty, since I think local declarations are
much more common in a procedure than in an accept statement. But that's a really
small spoon of syntactic sugar...

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

From: Dmitry Kazakov
Date: Thursday, March 12, 2009  4:16 AM

> Yes, sometimes.  So how about we have a shorthand notation:
> For a formal parameter, "X: procedure..." is syntactic sugar for "X:
> not null access procedure...". (Similar for "function".) And for an
> actual subprogram parameter, "Blah" is sugar for "Blah'Access".

My_Limited_Type is not sugar for access My_Limited_Type. Why procedural types
should be any different?

(There is a minor problem with distinguishing parameter passing an a call to
function. Calls to overloaded

   procedure Foo (Eager_X : Integer);
   procedure Foo (Lazy_X : function return Integer);

could be ambiguous without qualified expression.)

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

From: Yannick Duchene
Date: Thursday, March 12, 2009  4:55 AM

> But is this really worth the effort? If the object is to have the
> subprogram body at the point of the call, isn't
>
> declare
>     procedure Temp (Element : in out E) is
>        ...
>     begin -- Temp
>        ...
>     end Temp;
> begin
>     V.Update_Element (Position => 10, Process => Temp'access); end;
>
> sufficient?
>

Sufficient, yes, it will works, but it will not be as much nice.
What is needed is a nice and ellegant way to get it.
As pointed before "Temp" is not meaningful.
.... or it is : "Temp" could point a language flaw (joking).

Comparing the "for .... do ...." proposal to this one, the "for .... do ....."
one is as simple as this one, while beeing more strict.

I've got a kind of rule for a long : when a particular language construct is
very common it should get a dedicated support to avoid "programming kinds of
language constructs". This is the way humans languages works, and this is it
what makes them quickly understandable. A common construct or a common meaning
is given a dedicated construct, so that it is quickly identified as such. To
understand this code sample, requires some analysis while the "for .... do ...."
construct explicitly states what it is.

Of course, there is no formal rule which assert that a particular construct
should be given a dedicated support in a language (and not every one should get
one). It is a matter of weighting and overall evaluation.  The "for .... do
...." is not formaly required, but as it stands for a very common construct, it
appears as beeing useful at low cost, what is a good evaluation.

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

From: Yannick Duchene
Date: Thursday, March 12, 2009  5:16 AM

> (1) It's fortuitous that all of the Ada.Containers routines that take
>     an access-procedure parameter take it as the last parameter.  Or
>     maybe this was done deliberately in anticipation of Randy making a
>     proposal like this...?  Anyway, a possible way to relax this
>     restriction would be to allow a syntax something like this:
>
>      for My_Vector.Update_Element (Position => 10, Process => <>) do
> procedure
>                                        (Element : in out
> My_Element_Type) is

And so " Process => <> " could be at any position ?
This will help to reuse procedures whose " Process " (or equivalent) parameter
is not the last one. Relevant

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

From: Adam Beneschan
Date: Thursday, March 12, 2009  3:08 PM

> And so " Process => <> " could be at any position ?
> This will help to reuse procedures whose " Process " (or
> equivalent) parameter is not the last one.

Yes, that was the intent.

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

From: Yannick Duchene
Date: Thursday, March 12, 2009  5:36 AM

>> I don't usually find it hard to come up with good names for such
>> subprograms.
>> In
>> the original example, the name would be Increment. And if you feel
>> that such subprograms don't have or deserve good names, then using
>> the formal parameter name avoids any effort and reduces any
>> complexity in matching the reference to the subprogram to its
>> declaration.
>
> More, encouraging the programmer not to give a name to the procedure
> seems to me to be contrary to the Ada way.

With the proposal of allowing the parameter to appears as a named parameter (
Process => <> ), this is not true any more. This way, it is not more anonymous
than any literal given as actuals for named parameters in a procedure call.

While that's true that declaring literals as named constants is a better way...
true (means that naming the procedure is nice as well).

On the other hand, for peoples who use names as references only, as there is
only one reference, it can be anonymous without any troubles.

We may keep in minds too, that Ada profiles are still in the place and
availables for peoples who wish this (well, I'm neither sure this is a good
argument in this discussion).

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

From: Bob Duff
Date: Thursday, March 12, 2009  8:44 AM

> The initial part of the "procedure literal", up to the "begin", can
> clearly be implicit, giving the one-liner:
>
>     A.Update_Element (I, begin Element := Element + 1; end);

This goes too far -- the names of the parameters (Element in this case) really
need to be explicit.

Imagine this:

    procedure P (...) is
        Element : ...;
        ...
    begin
        ...
        A.Update_Element (I, begin Element := Element + 1; end);

So we have an imaginary implicit declaration of Element that hides the explicit one.  Very confusing.  This is exactly the same problem as the rightly-reviled "with" statement in Pascal.

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

From: Jeffery R. Carter
Date: Thursday, March 12, 2009  1:53 PM

>    A.Update_Element (I, begin Element := Element + 1; end);

But in some cases, local declarations may be desirable, which would give us

A.Update_Element (I, is ... begin ... end);

These both look rather like a block statement, so perhaps one could argue for

[declare ...] begin ... end

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

From: Niklas Holsti
Date: Thursday, March 12, 2009  2:22 PM

>>The initial part of the "procedure literal", up to the "begin", can
>>clearly be implicit, giving the one-liner:
>>
>>    A.Update_Element (I, begin Element := Element + 1; end);
>
>
> This goes too far -- the names of the parameters (Element in this
> case) really need to be explicit.

I can understand your objection as a point of style or taste, but I don't think
there is any technical or formal need for explicit parameters from the point of
view of language definition or implementation (except perhaps for resolving
overloading -- I don't know enough about that).

I would of course still allow the longer, unabbreviated form, with an explicit
parameter declaration. The choice would be the programmer's, perhaps enforced by
coding rules.

> Imagine this:
>
>     procedure P (...) is
>         Element : ...;
>         ...
>     begin
>         ...
>         A.Update_Element (I, begin Element := Element + 1; end);
>
> So we have an imaginary implicit declaration of Element that hides the
> explicit one.  Very confusing.  This is exactly the same problem as
> the rightly-reviled "with" statement in Pascal.

I find hiding to be confusing in general, and would choose to forbid it
completely, if I had my way. If my Ada compiler had an option to warn about
hiding I would always use it. Compilers already check hiding caused by multiple
"use" clauses. I don't understand why hiding by nested scopes should be allowed,
but it is of course too late to forbid it in Ada.

We could forbid hiding that comes from the proposed "imaginary implicit"
declarations, or at least forbid any use of names affected by such hiding. That
is analogous to hiding by multiple "use" clauses and would make your hiding
example illegal, and (perhaps) make both of us happy :-)

I like your reference to the Pascal "with". The reasons for using "with" in
Pascal, and the alternatives to "with" that one can use in Ada (renaming
declarations in a local block, or passing the record variables "in out" to a
helper procedure), have much in common with this discussion of Update_Element
and its relatives.

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

From: Adam Beneschan
Date: Thursday, March 12, 2009  2:59 PM

> > This goes too far -- the names of the parameters (Element in this
> > case) really need to be explicit.
>
> I can understand your objection as a point of style or taste, but I
> don't think there is any technical or formal need for explicit
> parameters from the point of view of language definition or
> implementation (except perhaps for resolving overloading -- I don't
> know enough about that).

I think Bob is thinking about the point of view of the poor sap who has to read
someone else's code and try to figure out what the heck is going on.

> I would of course still allow the longer, unabbreviated form, with an
> explicit parameter declaration. The choice would be the programmer's,
> perhaps enforced by coding rules.
>
> > Imagine this:
> >
> >     procedure P (...) is
> >         Element : ...;
> >         ...
> >     begin
> >         ...
> >         A.Update_Element (I, begin Element := Element + 1; end);
> >
> > So we have an imaginary implicit declaration of Element that hides
> > the explicit one.  Very confusing.  This is exactly the same problem
> > as the rightly-reviled "with" statement in Pascal.
>
> I find hiding to be confusing in general, and would choose to forbid
> it completely, if I had my way. If my Ada compiler had an option to
> warn about hiding I would always use it. Compilers already check
> hiding caused by multiple "use" clauses. I don't understand why hiding
> by nested scopes should be allowed

So that (1) if you want to add a new local variable to a nested procedure, you
don't have to go looking at all the variables declared in the outer procedure to
figure out what you're allowed to use; and (2) if you want to add a name to an
outer procedure or package, you can do so without looking at every nested scope
to see what you can or can't use.  If I understand correctly, then if hiding in
nested scopes weren't allowed, you couldn't add a new type name to the
specification of a package, without first looking at the implementations of all
procedures and functions in the package, and probably in child packages, to
figure out what names were unavailable for your new type name because that would
result in hiding.

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

From: Bob Duff
Date: Thursday, March 12, 2009  2:49 PM

> I can understand your objection as a point of style or taste, but I
> don't think there is any technical or formal need for explicit
> parameters from the point of view of language definition or
> implementation...

That's correct.  Your suggestion can be defined and implemented.
My objection is purely from a readability point of view.

>... (except perhaps for resolving overloading -- I don't  know enough
>about that).

No problem, there.  Parameter names can't be overloaded anyway.

> I would of course still allow the longer, unabbreviated form, with an
> explicit parameter declaration. The choice would be the programmer's,
> perhaps enforced by coding rules.

It's not usually a good idea to give programmers a choice between two
not-so-good alternatives.  One is too verbose, the other is confusing and less
readable.  A language designer who dumps this choice on the programmer is
shirking his duty.  If we can't choose, then how is a programmer supposed to be
able to choose.  ;-)

> > Imagine this:
> >
> >     procedure P (...) is
> >         Element : ...;
> >         ...
> >     begin
> >         ...
> >         A.Update_Element (I, begin Element := Element + 1; end);
> >
> > So we have an imaginary implicit declaration of Element that hides
> > the explicit one.  Very confusing.  This is exactly the same problem
> > as the rightly-reviled "with" statement in Pascal.
>
> I find hiding to be confusing in general, and would choose to forbid
> it completely, if I had my way.

I agree.

>...If my Ada compiler had an
> option to warn about hiding I would always use it.

Note that GNAT has such an option.

>...Compilers
> already check hiding caused by multiple "use" clauses.

Right.  The rules for "use" clauses are brilliant.  I'm not sure why Jean
Ichbiah didn't use the same rules everywhere.

>...I don't
> understand why hiding by nested scopes should be allowed, but it is
>of course too late to forbid it in Ada.
>
> We could forbid hiding that comes from the proposed "imaginary
> implicit" declarations, or at least forbid any use of names affected
> by such hiding. That is analogous to hiding by multiple "use" clauses
> and would make your hiding example illegal, and
> (perhaps) make both of us happy :-)

Sorry, I suppose it would make me "happier", but not "happy enough".
I'd still think it's important for readability to have the parameters declared
explicitly and locally.

> I like your reference to the Pascal "with". The reasons for using
> "with" in Pascal, and the alternatives to "with" that one can use in
> Ada (renaming declarations in a local block, or passing the record
> variables "in out" to a helper procedure), have much in common with
> this discussion of Update_Element and its relatives.

If Pascal's "with" used rules similar to Ada's "use", then it would be an
excellent feature (and I would be happy to have it in Ada).

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

From: Niklas Holsti
Date: Thursday, March 12, 2009  3:55 PM

> I think Bob is thinking about the point of view of the poor sap who
> has to read someone else's code and try to figure out what the heck is
> going on.

Yes, I understood that. I share his and your concern with readability, but if we
want to cut "verbiage" to a minimum, something must be sacrificed.

I would advocate using these abbreviated forms of actual subprogram parameters
only when the same operations (eg. Update_Element) are used in many places in
the same module so that their usage becomes idiomatic. "Use" clauses can be
defended under the same conditions, and otherwise cause the same kind of
readability problems.

> [ example of hiding ]
>>
>>I find hiding to be confusing in general, and would choose to forbid
>>it completely, if I had my way. ...  I don't understand why hiding by
>>nested scopes should be allowed
>
> So that (1) if you want to add a new local variable to a nested
> procedure, you don't have to go looking at all the variables declared
> in the outer procedure to figure out what you're allowed to use;

But if you add a hiding variable, you create a trap for the poor sap who later
reads the code in the nested procedure and will now have to understand that the
same name means different things in the inner and outer procedures.

> (2) if you want to add a name to an outer procedure or package, you
> can do so without looking at every nested scope to see what you can or
> can't use.

Again, if you allow hiding, you create a later readability problem.

And you would not really have to "look" at every nested scope; you would add the
name and compile, and the compiler would complain if there is a mismatch. Just
as it now complains if you add a name to a "used" package that conflicts with an
existing name in another "used" package.

> If I understand correctly, then if hiding in nested scopes weren't
> allowed, you couldn't add a new type name to the specification of a
> package, without first looking at the implementations of all
> procedures and functions in the package, and probably in child
> packages, to figure out what names were unavailable for your new type
> name because that would result in hiding.

Right. You have to take more care when writing the program, but reading becomes
easier.

Hiding by nested scoped was introduced when programmers used short variable
names like "i", "j", "x" and so on, and name clashes were quite likely. Today we
use longer names, and I think we could forbid hiding. But I admit that it is a
pretty radical proposal, and would be incompatible with some current naming
styles, for example those that define class hierarchies by the corresponding
child package hierarchy and define a "type Object" at every level.

A compromise would be to allow hiding, but forbid unqualified use of names that
clash. That would be similar to the rules for "use" clauses.

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

From: Adam Beneschan
Date: Thursday, March 12, 2009  4:11 PM

> But if you add a hiding variable, you create a trap for the poor sap
> who later reads the code in the nested procedure and will now have to
> understand that the same name means different things in the inner and
> outer procedures.

This is getting way off topic (particularly since there's zero chance anything
will be changed), but...

I don't see that as a problem.  If you have a procedure that declares a local
variable Blah, and the body refers to Blah, there generally isn't any confusion.
The name "Blah" refers to the variable whose declaration you're staring right
there at.  (Assuming procedures aren't overly long.  I can see how it could be a
problem if it were. But then it probably ought to be broken up anyway.)  It *is*
a problem when the name is declared only *implicitly*, as it is in Pascal's
"with" statement, or with parameter names in some of the proposals in this
thread.

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

From: Niklas Holsti
Date: Friday, March 13, 2009  3:37 AM

> This is getting way off topic (particularly since there's zero chance
> anything will be changed), but...

I agree. Except in relation to the proposals for "in-line"  or "literal"
actual-parameter subprograms.

> .. It *is* a problem when the name is declared only *implicitly*, as
> it is in Pascal's "with" statement, or with parameter names in some of
> the proposals in this thread.

I agree, again. The proposals for implicitly declared parameters came with
suggestions for mitigating or solving the problem by compiler warnings or by
forbidding the unqualified use of implicitly declared clashing names.

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

From: Georg Bauhaus
Date: Thursday, March 12, 2009  4:23 AM

>  "in-line"  or "literal" actual-parameter subprograms.

If the block that is to be the subprogram's "replacement"
gets a name, then it can call itself recursively.
(Could be a small state machine.) OTOH, since a loop body is not recursive,
there should therefore not be a named "replacement".

I'd think that the "... do ..." proposal has the necessary ingredients? Reading
the source, either expect ";" or "do" when some indicator like Process => <>
suggests there should be a "do ..." part.

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

From: Dmitry Kazakov
Date: Sunday, March 15, 2009  1:17 PM

I thought about making container iterations less "functional" and more
imperative. It seems to me that closures and procedural values are not much in
Ada spirit. So I propose for-statement syntax, where the loop body would serve
as the closure. In short:

   for Element in Repeater loop
      ... -- Things we do
   end loop

The proposal does not require changes in Ada.Containers. If you don't like "in"
it could be "at".

The proposal:
--------------------------------------
1. The short form:

   for <defining_identifier> in <procedure_call_statement> loop
        <statement_sequence>
   end loop;

I don't want to describe it formally, because it is easier to understand on the
Update_Element example:

   for Element in My_Vector.Update_Element loop
      if Element = Wow then
         Element := Whee;
      end if;
   end loop;

The whole is an abbreviation of

   declare
      procedure Loop_Body (Element : in out My_Element_Type) is
      begin
         if Element = Wow then
            Element := Whee;
         end if;
      end Loop_Body;
   begin
      My_Vector.Update_Element (Process => Loop_Body);
   end;

1.2. The <procedure_call_statement> can have keyed parameter association:

   for Element in
      Update_Element (Vector => My_Vector, Process => loop) loop
      if Element = Wow then
         Element := Whee;
      end if;
   end loop;

when loop has label then:

A: for Element in
      Update_Element (Vector => My_Vector, Process => loop A) loop
      if Element = Wow then
         Element := Whee;
      end if;
   end loop A;

1.3. The loop body matches formal procedures with profiles:

   procedure (X : in T)
   procedure (X : out T)
   procedure (X : in out T)

the latter two cases make <defining_identifier> mutable within the loop.
--------------------------------
2. The full form:

   for <parameter_profile>|<parameter_and_result_profile>
      in <procedure_call_statement> loop
        <statement_sequence>
   end loop;

When <parameter_and_result_profile> is used then the loop body shall contain
return statement.

Example:

given

 procedure Integrate (
   X0, X1 : Float;
   not null access function F (X : Float) return Float;
   S : out Float);

and

   Sum : Float := 0.0;

then

   for (X : Float) return Float in Integrate (0.0, 1.0, S) loop
      return Gamma (X);  -- integrates Gamma-function
   end loop;

In parameter association F => loop can be always omitted.

[Well, I don't like return in the loop body, so I am looking for something
better, which can handle unconstrained types]

[It would be nice to be able to use iterating functions. E.g. when Integrate
would be a function returning Sum. I considered the loop name to name the
result, but it does not fit into a sequence of statements. A better idea is
required.]

------------------------------
3. Exit statements and gotos (optional):

   for Element in My_Vector.Update_Element loop
      exit when Element;
      Element := Whee;
   end loop;

means

   begin
      for Element in My_Vector.Update_Element loop
         exit when Element;
         Element := Whee;
      end loop;
   exception
      when <anonymous_exit_exception>;
          null;
   end;

Exiting inner iteration loop

A: loop
      for Element in My_Vector.Update_Element loop
         exit A when Element;
         Element := Whee;
      end loop;

means

A: loop
      begin
         for Element in My_Vector.Update_Element loop
            exit when Element;
            Element := Whee;
         end loop;
      exception
         when <anonymous_exit_exception>;
             exit A;
      end;

------------------------------
4. Extension for array types (optional):

   for (X : Integer; Y : Integer) in Matrix loop
      Sum := Sum + Matrix (X, Y);
   end loop;

4.1. The order of iteration is not defined (differently to the standard for ...
A'Range)
4.2. The implementation is allowed to perform iterations in parallel on
multi-core architectures.

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


Questions? Ask the ACAA Technical Agent