Version 1.1 of ai12s/ai12-0190-1.txt

Unformatted version of ai12s/ai12-0190-1.txt version 1.1
Other versions for file ai12s/ai12-0190-1.txt

!standard 5.5.2(2/3)          15-06-02 AI12-0190-1/01
!class Amendment 16-06-02
!status work item 16-06-02
!status received 16-05-06
!priority Low
!difficulty Medium
!subject Lambda functions
!summary
** TBD.
!problem
"others at AdaCore feel that lambdas are critically important".
[Editor's note: I can find no problem statement anywhere, and I don't know of any problem, either. If this had been an Ada Comment posting, we'd have tossed it directly into the trash because of a lack of a problem statement...]
!proposal
A lambda_function can be used in a context where the expected type is a single access-to-function type (or as the actual parameter for a formal function). It is roughly equivalent to using the name'Access (or simply name) of an expression function which is declared at the point of use, with parameter types, modes, etc. taken from that of the expected access-to-function type (or the formal function). Only the formal parameter names come from the lambda function.
primary ::= lambda_function
lambda_function ::= ( LAMBDA [ lambda_parameter_list ] expression )
lambda_parameter_list ::= ( identifier { , identifier } )
!wording
** TBD.
!discussion
** TBD.
!example
-- Replace control chars with '?' Ada.Strings.Fixed.Translate
(Str, (lambda (C) (if C < ' ' then '?' else C)));
-- Procedure for plotting a function procedure Plot_Graph
(Fun : access function (X : Float) return Float;
Start, Stop : Float);
...
-- Plot a graph of X-squared from 1 to 20 Plot_Graph (Fun => (lambda (Z) Z**2), Start => 1.0, Stop => 20.0);
!ASIS
** TBD.
!ACATS test
An ACATS C-Test is needed to check that the new capabilities are supported.
!appendix

From: Tucker Taft
Sent: Friday, May 6, 2016  2:05 PM

Now that we have a number of language-defined subprograms that take
access-to-subprogram parameters, it seems worth considering supporting some kind
of "anonymous" function/procedures.  Here are two proposals, one for anonymous
(lambda) functions, and one for anonymous (loop-body) procedures:

[Editor's note: See AI12-0189-1 for the other proposal.]

Lambda functions:

A lambda_function can be used in a context where the expected type is a single
access-to-function type (or as the actual parameter for a formal function).  It
is roughly equivalent to using the name'Access (or simply name) of an expression
function which is declared at the point of use, with parameter types, modes,
etc. taken from that of the expected access-to-function type (or the formal
function).  Only the formal parameter names come from the lambda function.

    primary ::= lambda_function

    lambda_function ::= ( LAMBDA [ lambda_parameter_list ] expression )

    lambda_parameter_list ::= ( identifier { , identifier } )

Here is an example:

    --  Replace control chars with '?'
    Ada.Strings.Fixed.Translate
      (Str, (lambda (C) (if C < ' ' then '?' else C)));

    --  Procedure for plotting a function
    procedure Plot_Graph
      (Fun : access function (X : Float) return Float;
       Start, Stop : Float);

    ...

    --  Plot a graph of X-squared from 1 to 20
    Plot_Graph (Fun => (lambda (Z) Z**2), Start => 1.0, Stop => 20.0);

=====================

Again, if there is interest, I can write up these ideas as AIs.  As mentioned,
we discussed the loop-body procedures a bit in Vermont.

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

From: Brad Moore
Sent: Saturday, May 7, 2016  8:40 AM

I'm definitely interested in seeing some improvements in this area, but I'd like
to see a single, more general solution that covers both these cases (expression
functions and iterators) as well as other cases. Essentially, I'd like to be
able to provide an anonymous subprogram for any parameter of an
access-to-subprogram type (not just expression functions), and possibly other
places such as generic formal subprograms.

This capability already exists in C++, C#, and Java in some form.

The idea here is to write the full body of the subprogram inline, including the
parameter profile, but omitting the name for the subprogram.

Tuckers examples might look like;

-- Example using an expression function
Ada.Strings.Fixed.Translate
   (Source => Str,
    Mapping => (C : Character) is (if C < ' ' then '?' else C));

--  Procedure for plotting a function
procedure Plot_Graph
   (Fun : access function (X : Float) return Float;
    Start, Stop : Float);

    ...

--  Another expression function
--  Plot a graph of X-squared from 1 to 20 Plot_Graph (Fun => (Z : Float) is (Z**2)),
             Start => 1.0, Stop => 20.0);


-- Example using a procedure.
-- Iterating via Ada.Environment_Variables Ada.Environment_Variables.Iterate
    (Process => (Name, Value : String) is
     begin
        Put_Line (Name & " => " & Val);
     end);

-- Another example showing not just for iteration and expression
-- functions

-- procedure for executing N Blocks in parallel procedure Parallel_Blocks
   (Number_Of_Blocks : Positive;
    Process : not null access procedure (Worker : Positive));

-- Call to execute two procedures in parallel using inline procedure
-- featuring a declaration
Parallel_Blocks (Number_Of_Blocks => 2,
                  Process => (Worker : Positive) is
    declare
       X : Integer := Foo;
    begin
       if Worker = 1 then
         Do_Lengthy_Processing_A (X);
       else
         Do_Other_Processing (X);
       end if;
    end);

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

From: Tucker Taft
Sent: Saturday, May 7, 2016  2:17 PM

> The idea here is to write the full body of the subprogram inline,
> including the parameter profile, but omitting the name for the subprogram. ...

I believe readability goes down if you start embedding statements inside
expressions. Since you can always declare a local subprogram, trying to bury a
sequence of statements inside an expression is unnecessary, and doesn't really
improve "writability" much either.

Also, it is always redundant to specify the parameter types for a lambda
function, given that they would only be allowed in contexts where the expected
profile is known.

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

From: Jean-Pierre Rosen
Sent: Saturday, May 7, 2016  2:29 PM

But this kind of redundancy appears often in Ada, on the ground that the reader
should not have to wander to far-away places to get important information like
types.

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

From: Tucker Taft
Sent: Saturday, May 7, 2016  2:41 PM

Well, I suppose it would be easy enough to allow a full formal parameter part in
the "lambda" function syntax, as in the proposed syntax for the loop-body
procedure.  But I certainly wouldn't want to require it.

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

From: John Barnes
Sent: Sunday, May 8, 2016  4:15 AM

I am always wary of mixing expressions and statements. I went to a lecture on
lambda expessions by Peter Landin at a Summer School in Oxford in 1963. I must
read the notes.

I am in favour provided it deosn't cause confusion and/or make the next version
of my book much bigger.

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

From: Erhard Ploedereder
Sent: Monday, May 9, 2016  9:18 AM

> Also, it is always redundant to specify the parameter types for a
> lambda function, given that they would only be allowed in contexts
> where the expected profile is known.

But this would mean that lambdas cannot take lambdas as arguments, would it not?
(Which would really turn off the functional crowd).

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

From: Tucker Taft
Sent: Monday, May 9, 2016  10:27 AM

I don't follow this.  If the profile of the lambda is known, then so is the
profile of any arguments of the lambda that are themselves access-to-functions,
so nesting should be no problem.  If you can show an example, I can verify this
claim, but I will admit I am not up to producing a realistic example where a
lambda takes a lambda.

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

From: Edmond Schonberg
Sent: Monday, May 9, 2016  10:42 AM

If nothing else, how do you disambiguate  F (X) within the expression if F is a
lambda parameter?  An array of Xs, a a type with implicit dereference, or a
call?

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

From: Tucker Taft
Sent: Monday, May 9, 2016  10:52 PM

You know the (access-to-function) type of F by context.

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

From: Jeff Cousins
Sent: Tuesday, May 10, 2016  4:04 AM

Lambda functions seem to me a bit like a WIBNI (wouldn't it be nice if) that, if
we have at all, should be tucked away in some optional annex. Would lambda be a
new reserved word?

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

From: Tucker Taft
Sent: Thursday, May 12, 2016  7:48 PM

Yes, I would recommend a new reserved word, as just trying to do lambda
functions with clever syntax would be harder to read, in my view.  In my view
the loop-body procedures are more important than lambda functions, but others at
AdaCore feel that lambdas are critically important.  But Randy reminded me that
Bob Duff has at least something like this on his plate, so I'll let him take a
stab at it first if he so desires.

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

From: Randy Brukardt
Sent: Thursday, May 12, 2016  5:21 PM

> Again, if there is interest, I can write up these ideas as AIs.  As
> mentioned, we discussed the loop-body procedures a bit in Vermont.

Right, and this topic was assigned to Bob Duff (certainly the second idea, and
arguably the first as well). Given your well-known huge pile of homework, I
recommend that you let Bob write the AI(s) on this topic. (If he doesn't do it,
we can revisit, there certainly isn't any rush at this point.)

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

From: Erhard Ploedereder
Sent: Thursday, May 12, 2016  6:30 PM

You are right. If the context of lambda construction always provides a specified
access-to-function type (obviously including its parameters), calling lambdas
with lambdas as arguments is not a problem.

The problem appears when lambdas can be named and lead an independent existence,
as in
   inc = lambda(x) (x + 1);
   conc = lambda(x) (x & "abc");
   twice = lambda(f, x) (f(f(x));
Now, we know nothing about the type of f (short of doing a very general type
inference) but we can say twice(inc, 5), while twice(conc, 5) should be illegal,
but how to tell without said type inference?

But you are not proposing named lambdas, so all is fine.

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

From: Randy Brukardt
Sent: Thursday, May 12, 2016  6:51 PM

> But you are not proposing named lambdas, so all is fine.

Sorry to toss out a bit of ignorance here, but what is the point of a "named
lambda"? Isn't that the same silliness as a "named anonymous access type"
(something we rightfully decided was too silly to pursue)?

It seems to me that a "named lambda" is essentially the same thing as a normal
subprogram. In your above example, the parameters are typeless, but if that's
allowed at all, it should be allowed everywhere (thus there is no difference
between a "named lambda" and any other kind of named subprogram).

If we were to do a more general lambda facility (which I've against for
readability reasons, but that's not relevant to this discussion), then surely
the context would have to identify an access-to-subprogram type (nothing being
typeless in Ada). The above sort of thing would be possible:

    Inc : constant access function (X : Integer) return Integer := (lambda(X) X + 1);
    Conc : constant access function (X : String) return String := (lambda(X) X & "abc");

And this would essentially be a "named lambda". And not appreciably different
than:

    function Inc (X : Integer) return Integer is (X + 1);
    function Conc (X : String) return String is (X & "abc");

(Which I hope most programmers would prefer.)

So the operative thing in your example that causes trouble is typeless-ness, not
whether or not the lambda is named in some sense. But Tucker (nor Brad for that
matter) were proposing any sort of typeless-ness. So I don't think there is any
problem.

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

From: Brad Moore
Sent: Thursday, May 12, 2016  7:56 PM

> Sorry to toss out a bit of ignorance here, but what is the point of a
> "named lambda"? Isn't that the same silliness as a "named anonymous access type"
> (something we rightfully decided was too silly to pursue)?

I haven't the definitive answer to this question, but I think I have a good
guess.

In such languages as C++ and Java, lambdas provide another missing capability.
They provide a means to essentially write a nested subprogram, which is
important because such subprograms can access variables in the enclosing scope.

Ada doesn't need that capability, because it has had it from day one, in Ada 83
with nested subprograms.

I see a named lambda being particularly useful in C++ because it might be needed
more than once, possibly passed as parameters into 2 or more separate function
calls. Rather than write the same lambda function multiple times, the named
lambda allows C++ programmers to write the function once, and then pass that
lambda into the separate call sites.

Ada doesn't seem to need that capability, because one can just write a nested
subprogram and pass that in, instead of using a lambda.

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

From: Randy Brukardt
Sent: Thursday, May 12, 2016  8:12 PM

On top of which, one can "name" a lambda as I showed (using an object with an
anonymous-access-to-subprogram). So if you really need two copies of a lambda,
that's a way to do it. (But just using a subprogram makes more sense anyway.)

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

From: Randy Brukardt
Sent: Thursday, May 12, 2016  9:20 PM

[Editor's note: From another thread, see AI12-0188-1.]

...
> I agree that some more discussion is needed.

Surely. I have to wonder why "others at AdaCore feel that lambdas are
critically important". Why is it so hard to stick an expression function in a
declare block? That's 20 extra characters or so?? It seems I'm missing
something (or perhaps that lambda proponents are, because none of the proposals
are going to weaken typing).

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

From: Erhard Ploedereder
Sent: Saturday, May 14, 2016  2:30 PM

> Sorry to toss out a bit of ignorance here, but what is the point of a
> "named lambda"?

It is the corner stone of functional programming in all the functional
languages. So people coming from a functional background are looking for that.

It is not quite a named subprogram. It is subclass, namely a named expression
function.

I agree with the remainder of what you wrote.

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

From: Brad Moore
Sent: Thursday, May 12, 2016  8:19 PM

>> ... I'm really dubious that a separate feature is warranted for this.
>
> I think you are in the minority on this one.

Just for comparison, two new syntax variants with specific usage for iterating
through map-like containers ...

    for (Key => Elem) of Container loop
       ...
    end loop;

and

   for (Key => <>) of Container loop
      ...
   end loop;

vs a single syntax capability that is much more general purpose including
other usages that have been discussed in the lambda thread...

    Container.Iterate (Key is
       begin
          ...
       end);

    Container.Iterate((Key, Element) is
       begin
          ...
       end);

Both proposals seem relatively similar in terms of ease of expression.

With specific syntax, there is a danger that more people will not know of its
existence, or remember its existence when a possibility for usage arises. With
a more general solution, chances are more people will think of using the
capability.

So far, I am agreeing with Randy here. I think most people are interested in a
solution, but not necessarily settled on the proposed solution.

   But in any case it sounds
> like we should discuss the various alternatives in an ARG meeting
> before I spend time writing it up, given the amount of other homework
> still on my plate...

I agree that some more discussion is needed.

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

From: Randy Brukardt
Sent: Thursday, May 12, 2016  9:20 PM

> Both proposals seem relatively similar in terms of ease of expression.

Well, I was replying to Tucker's "loop procedure" proposal, which looks like a
loop. (I don't think anyone other than yourself is interested in your general
proposal). Tucker's suggestion (based on his original message and an
appropriate Iterate) would look like:

    for (Key, Element) of Container.Iterate(<>) loop
       ...
    end loop;

which is so close to his first case that it would be madness to provide both.
[And the <> is optional above.]

And it is easy to imagine extending his original idea to allow using <> in
place of parameters not wanted (they'd still exist, but be anonymous), thus
getting:

    for (Key, <>) of Container.Iterate loop
       ...
    end loop;

which handles the second case perfectly.

> With specific syntax, there is a danger that more people will not know
> of its existence, or remember its existence when a possibility for
> usage arises. With a more general solution, chances are more people
> will think of using the capability.

Or even more likely never even consider it because they can't grok lambdas.

I'm in favor of Tucker's loop syntax as given above (it's natural and could be
used for all of the containers). Indeed, it's a better idea than the existing
cursor iterators, which we may not have defined at all if we had the above.

I'm pretty much against all of the other proposals, I don't see the value of
the complications (especially as access-to-anything ought to be minimized,
especially in reusable code). And the idea of sticking statements in the
middle of expressions is a bridge too far to me. All of the languages that you
listed as having lambdas seem to me to be also those languages that are
actively against readability!

I'm also getting worried about feature creep. We expanded expressions to
include if and case and quantified and expression functions because of the
needs of contract aspects. I don't know what is supposed to be driving
lambdas.

...
> I agree that some more discussion is needed.

Surely. I have to wonder why "others at AdaCore feel that lambdas are
critically important". Why is it so hard to stick an expression function in a
declare block? That's 20 extra characters or so?? It seems I'm missing
something (or perhaps that lambda proponents are, because none of the proposals
are going to weaken typing).

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

From: Brad Moore
Sent: Thursday, May 12, 2016  10:55 PM

Anonymous functions are fairly common in programming languages, and apparently
can be found in languages including C++, C#, Dart, Erlang, Go, Haskell, Java,
Javascript, List, Lua, Mathematica, Perl, PHP, Python, Ruby, Scala, Smalltalk,
Swift, ...

https://en.wikipedia.org/wiki/Anonymous_function

There must be at least some folks out there who grok lambdas.

Well, I have one more example, that is kind of interesting that I plan to use
for a tutorial at Ada Europe... Interesting here not from the standpoint of
parallelism, but from the standpoint of being called from multiple languages,
and involving multiple anonymous subprogram parameters.

The following non-generic Ada subprogram that can be called from multiple
languages including C, C++, C#, and Java, as it exports the C calling convention
for the C and C++ case, and can be easily ported to C# and Java as well, which I
have done using the GNAT dot net and java compilers....

procedure Parallel_Loop
   (From           : Loop_Index;
    To             : Loop_Index;
    Reset          : not null access procedure;
    Loop_Body      : not null access
      procedure (Start, Finish : Loop_Index);
    Reduce         : not null access procedure);

Basically,
    Reset is used to reinitialize task local variables,
    Loop_Body does the processing of the loop,
    and Reduce combines results produced by the Loop_Body.

Currently to call this routine from Ada to calculate the sum of numbers from 1
to 1_000_000, one might write in Ada....

    package Partial_Sums is new
       Ada.Task_Attributes (Attribute     => Integer,
                            Initial_Value => 0);
    procedure Reset is
    begin
       Partial_Sums.Set_Value (0);
    end Reset;

    procedure Compute_Sum
      (Start, Finish : Parallel.Loop_Index)
    is
       Partial_Sum : Integer renames Partial_Sums.Reference.all;
    begin
       for I in Start .. Finish loop
          Partial_Sum := Partial_Sum + I;
       end loop;
    end Compute_Sum;

    Sum : Integer := 0;

    procedure Reduce () is
    begin
       Sum := Sum + Partial_Sums.Value;
    end Reduce;

    Reducing_Loops.Work_Seeking.Parallel_Loop
      (From           => 1,
       To             => 1_000_000,
       Reset          => Reset'Access,
       Process        => Compute_Sum'Access,
       Reduce         => Reduce'Access);

To call this same Ada library from C#, one can currently write...

    [ThreadStatic]
    private static int partial_sum;
    ...
       int sum = 0;

       paraffin_pkg.parallel_loop
          (from   : 1,
           to     : 1000000,
           reset  : () => { partial_sum = 0; },
           process: (start, finish) =>
             {
               for (int i = start; i <= finish; i++)
                  partial_sum += i;
             },
           Reduce : () => { sum += partial_sum; });

Which of these two versions do you find more readable?

I prefer the C# version mostly because there is less "noise" text, and the
formal parameters of the call are directly associated with the code. In Ada, you
are first presented with a bunch of routines, not knowing their purpose, until
you get down to the call, but then your eyes have to jump up and down to
associate the logic with the call site.

In this example, it appears to be actually easier to call Ada code from C#, than
from Ada, I would say.

If instead, some sort of general lambda feature existed in Ada, I might be able
to write something like...

package Partial_Sums is new
       Ada.Task_Attributes (Attribute     => Integer,
                            Initial_Value => 0);
    Sum : Integer := 0;

    Reducing_Loops.Work_Seeking.Parallel_Loop
      (From     => 1,
       To       => 1_000_000,
       Reset    => is Partial_Sums.Set_Value (0),
       Process  => (Start, Finish) is
          Partial_Sum : constant access Integer :=
             Partial_Sums.Reference;
         begin
            for I in Start .. Finish loop
               Partial_Sum.all := Partial_Sum.all + I;
            end loop;
         end,
       Reduce   => is Sum := Sum + Partial_Sums.Value);

Which I find more readable, but maybe thats just me...

>
> I'm also getting worried about feature creep. We expanded expressions
> to include if and case and quantified and expression functions because
> of the needs of contract aspects. I don't know what is supposed to be
> driving lambdas.

I suppose its mostly just syntactic sugar to hopefully improve readability with
a short hand form of expression, in the same way that loop iterators of Ada 2012
did that for loops.

Also I think it would be beneficial to be able to say that Ada provides a lambda
feature comparable to most other main stream languages.

If I am alone in seeing the benefit of this, then I wont bother promoting the
idea, but then I tend to agree with the worry of feature creep for special
purpose syntax that has limited use. My concern would be to wonder if the same
capability can be provided reasonably with just a library addition.

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

From: Randy Brukardt
Sent: Friday, May 13, 2016  12:04 AM

A couple of thoughts rather than a complete response to your message because its
important to hear more from the rest of the group before beating to death ideas
that may not have much support anyway:

...
>> I'm pretty much against all of the other proposals, I don't see the
>> value of the complications (especially as access-to-anything ought to
>> be minimized, especially in reusable code). And the idea of sticking
>> statements in the middle of expressions is a bridge too far to me.
>> All of the languages that you listed as having lambdas seem to me to
>> be also those languages that are actively against readability!

>Anonymous functions are fairly common in programming languages, and apparently
>can be found in languages including C++, C#, Dart, Erlang, Go, Haskell, Java,
>Javascript, List, Lua, Mathematica, Perl, PHP, Python, Ruby, Scala, Smalltalk,
>Swift, ...
>
>https://en.wikipedia.org/wiki/Anonymous_function

We've come to regret almost everything anonymous that has ever been in or added
to Ada. They tend to cause all kinds of definitional and usage problems. I'm
quite opposed to repeating that mistake; every time we've been told how it will
all work out fine, and it never does.

What does help is to have short-hand syntax (like generalized references and
generalized indexing) where named stuff can be used with a shorthand.

Besides, what is really going on in most of those languages is some sort of
poor-man's subprogram type and values. The benefit isn't in anonymity but rather
in having subprogram values. We'd be better off looking in that direction if we
really needed it (but still, all of the subprogram types and values should have
names).

...
> The following non-generic Ada subprogram that can be called from
> multiple languages including C, C++, C#, and Java, as it exports the C
> calling convention for the C and C++ case, and can be easily ported to
> C# and Java as well, which I have done using the GNAT dot net and java
> compilers....
>
> procedure Parallel_Loop
>    (From           : Loop_Index;
>     To             : Loop_Index;
>     Reset          : not null access procedure;
>     Loop_Body      : not null access
>       procedure (Start, Finish : Loop_Index);
>     Reduce         : not null access procedure);
>
> Basically,
>     Reset is used to reinitialize task local variables,
>     Loop_Body does the processing of the loop,
>     and Reduce combines results produced by the Loop_Body.
>
> Currently to call this routine from Ada to calculate the sum of
> numbers from 1 to 1_000_000, one might write in Ada....
>
>     package Partial_Sums is new
>        Ada.Task_Attributes (Attribute     => Integer,
>                             Initial_Value => 0);
>     procedure Reset is
>     begin
>        Partial_Sums.Set_Value (0);
>     end Reset;
>
>     procedure Compute_Sum
>       (Start, Finish : Parallel.Loop_Index)
>     is
>        Partial_Sum : Integer renames Partial_Sums.Reference.all;
>     begin
>        for I in Start .. Finish loop
>           Partial_Sum := Partial_Sum + I;
>        end loop;
>     end Compute_Sum;
>
>     Sum : Integer := 0;
>
>     procedure Reduce () is
>     begin
>        Sum := Sum + Partial_Sums.Value;
>     end Reduce;
>
>     Reducing_Loops.Work_Seeking.Parallel_Loop
>       (From           => 1,
>        To             => 1_000_000,
>        Reset          => Reset'Access,
>        Process        => Compute_Sum'Access,
>        Reduce         => Reduce'Access);
>
> To call this same Ada library from C#, one can currently write...
>
>     [ThreadStatic]
>     private static int partial_sum;
>     ...
>        int sum = 0;
>
>        paraffin_pkg.parallel_loop
>           (from   : 1,
>            to     : 1000000,
>            reset  : () => { partial_sum = 0; },
>            process: (start, finish) =>
>              {
>                for (int i = start; i <= finish; i++)
>                   partial_sum += i;
>              },
>            Reduce : () => { sum += partial_sum; });
>
> Which of these two versions do you find more readable?

Neither. A loop should look like a loop; otherwise causal reading/tools will not
discover where the majority of the work is happening.

Tucker's "loop procedure" proposal does look like a loop; it would make this
look something like:

   for (First, Last) of Reducing_Loops.Work_Seeking.Parallel_Loop
       (From           => 1,
        To             => 1_000_000,
        Reset          => Reset'Access,
        Process        => <>,
        Reduce         => Reduce'Access) loop
      declare
        Partial_Sum : Integer renames Partial_Sums.Reference.all;
      begin
        for I in Start .. Finish loop
           Partial_Sum := Partial_Sum + I;
        end loop;
      end;
   end loop;

...and the rename inside the loop body makes it messier. (You still have the
instance and other subprograms, which to me demonstrate that the original
routine is too complex to be used; I'd never bother trying to understand a
routine that takes THREE subprograms. At least the intent is that parallel loops
are directly supported by Ada syntax and the compiler - no subprograms -
anonymous or otherwise - anywhere.)

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

From: Brad Moore
Sent: Friday, May 13, 2016  12:32 AM

...
> Tucker's "loop procedure" proposal does look like a loop; it would
> make this look something like:
>
>     for (First, Last) of Reducing_Loops.Work_Seeking.Parallel_Loop
>         (From           => 1,
>          To             => 1_000_000,
>          Reset          => Reset'Access,
>          Process        => <>,
>          Reduce         => Reduce'Access) loop
>        declare
>          Partial_Sum : Integer renames Partial_Sums.Reference.all;
>        begin
>          for I in Start .. Finish loop
>             Partial_Sum := Partial_Sum + I;
>          end loop;
>        end;
>     end loop;

Ok, I didn't realize Tucker's solution could be applied to this case...
I still worry that people coming from C++, Java, C# etc, might criticize that we
didn't go far enough to allow Reset and Reduce to also be "inlined", or that
other sorts of non-loop cases can't be lambdaized, but I would like to hear
others comments on this as well. John said he had some notes about statements
within expressions from some time ago that would be good to hear.

> ...and the rename inside the loop body makes it messier. (You still
> have the instance and other subprograms, which to me demonstrate that
> the original routine is too complex to be used; I'd never bother
> trying to understand a routine that takes THREE subprograms.

Just thought it would be worth pointing out that C#'s current official
parallelism approach actually looks very similar to the library I have. Their
library also takes three subprograms....

Here is how one would do the same loop using the C# "standard" libraries.

int sum = 0;

Parallel.ForEach
    (source      : Partitioner.Create(1, 1000001),
     LocalInit   : () => 0,        // Reset
     body        : (range, loopState, local) =>
        {
          for (int i = range.Item1; i < range.Item2; i++)
            { local += i; }
          return local;
        },
     localFinally: local =>        // Reduce
        {Interlocked.Add(ref sum, local);});

Its a similar looking library routine, except that it is genericized so that the
thread local variables are passed into the body lambda, instead of having to be
declared separately, and that the programmer has to supply locking around the
code in the Reduce routine.

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

From: Jean-Pierre Rosen
Sent: Friday, May 13, 2016  2:23 AM

> I still worry that people coming from C++, Java, C# etc, might
> criticize that we didn't go far enough to allow Reset and Reduce to
> also be "inlined", or that other sorts of non-loop cases can't be
> lambdaized
<rant>
We've been trying for 30 years to find the killer feature that would attract
those people. The truth is that they don't intend to switch to Ada, and that
their criticizing is just for finding excuses for not switching languages. Ada
has its own strengthes, most of which reside in /not/ doing things the same way
as other languages.

So I suggest we just close our ears to these criticisms </rant>

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

From: Jeff Cousins
Sent: Friday, May 12, 2016  2:55 AM

...
> We've come to regret almost everything anonymous that has ever been in
> or added to Ada. They tend to cause all kinds of definitional and
> usage problems. I'm quite opposed to repeating that mistake; every
> time we've been told how it will all work out fine, and it never does.

John B certainly regrets most of the anonymous stuff, and purged a lot of it
when he revised his book.

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

[Editor's note: This thread continues in AI12-0189-1, since it is turning back
to loops.]

Questions? Ask the ACAA Technical Agent