Version 1.3 of ai12s/ai12-0188-1.txt

Unformatted version of ai12s/ai12-0188-1.txt version 1.3
Other versions for file ai12s/ai12-0188-1.txt

!standard 5.5.2(2/3)          16-06-02 AI12-0188-1/01
!class Amendment 16-06-02
!status No Action (7-0-0) 18-06-24
!status work item 16-06-02
!status received 16-05-06
!priority Very_Low
!comment Microscopic isn't allowed here. - Editor.
!difficulty Medium
!subject Add container iterator form supporting iteration over keys
!summary
** TBD.
!problem
Currently, the iterator form for iterating over containers only allows iteration over the elements of a container:
for Elem of Container loop ... end loop;
We have found many places where iterating over the keys of a map-like container is preferable.
!proposal
Add the following syntaxes:
for (Key => Elem) of Container loop
...
end loop;
and
for (Key => <>) of Container loop
...
end loop;
which provide, respectively, iteration over Key=>Elem pairs, and iteration over just keys.
In both forms, the Key is a constant. In the first form, the Elem would
be a variable if and only if the view of the Container is a variable.
!wording
** TBD.
!discussion
Iterating using a cursor provides this capability without a complex new facility, but of course less conviniently. The facility proposed in AI12-0189-1 also can provide this capability (but new subprograms would need to be defined in the Map and Set containers).
!ASIS
!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  9:08 AM

Currently, the iterator form for iterating over containers only allows
iteration over the elements of a container:

   for Elem of Container loop
      ...
   end loop;

We have found many places where iterating over the keys of a map-like
container is preferable.  Hence, I would recommend we add the following
syntaxes:

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

and

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

which provide, respectively, iteration over Key=>Elem pairs, and iteration
over just keys.
  In both forms, the Key is a constant.  In the first form, the Elem would
be a variable if and only if the view of the Container is a variable.

I'll write up a full AI if this general idea makes sense to others.

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

From: Gary Dismukes
Sent: Friday, May 6, 2016  1:19 PM

The proposal sounds reasonable.  I vote in favor of writing up an AI.

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

From: Jeff Cousins
Sent: Friday, May 6, 2016  11:06 AM

I hadn't thought about the syntax, but  I had been intending to suggest
iterating over the keys of maps, and I think John Barnes would like it too.
So I would welcome an AI.

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

From: John Barnes
Sent: Tuesday, May 10, 2016  4:50 AM

Hear hear!

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

From: Randy Brukardt
Sent: Tuesday, May 10, 2016  6:59 PM

> Currently, the iterator form for iterating over containers only allows
> iteration over the elements of a container:
>
>    for Elem of Container loop
>       ...
>    end loop;

This is certainly not true; the cursor form of iteration ought to be preferred
in many circumstances (this being one of them). Indeed, the "of" form is a
shorthand for various common cases; under no circumstances should it be
considered the only way to do iteration.

Secondly, it's possible to define an appropriate cursor iterator where the
"cursors" are actually keys (that assumes that the keys are definite and
nonlimited, as many will be). One "simply" instantiates the iterator generic
appropriately:

   package Key_Iterators is new Ada.Iterator_Interfaces (Cursor => Key,
                                            Has_Element => Has_Element);

and then defines an appropriate iterator type:

   type Key_Iterator is new Key_Iterators.Forward_Iterator with private;

This does require defining appropriate First, Next, and Has_Element routines
(and probably using the Rosen technique in Key_Iterator). But that's hardly
more work than will be needed to use the below (assuming a new kind of
interface is used).

> We have found many places where iterating over the keys of a map-like
> container is preferable.  Hence, I would recommend we add the
> following syntaxes:
>
>    for (Key => Elem) of Container loop
>       ...
>    end loop;
>
> and
>
>    for (Key => <>) of Container loop
>       ...
>    end loop;
>
> which provide, respectively, iteration over Key=>Elem pairs, and
> iteration over just keys.
>   In both forms, the Key is a constant.  In the first form, the Elem
> would be a variable if and only if the view of the Container is a
> variable.

There'd be some value to this for indefinite keys (i.e. String), but...

You'd have to have some additional operations somewhere to get the Key --
there aren't any of those currently in the iterator interfaces. And you're
surely not allowed to get those out of thin air. Either you need new iterator
interfaces, or new type aspects similar to Default_Iterator.
Substantially more complicated than just some new syntax (and when is new
syntax simple)?

So, while the idea is sound, it barely even rises to the level of nice-to-have.
We shouldn't be loading the next Ada up with nice-to-haves; we need other
non-AdaCore vendors to catch up and we did plenty of nice-to-haves the last
time around. (And this is the sort of thing that gives "committee-designed"
languages a bad name -- everyone's pet projects get included.)

> I'll write up a full AI if this general idea makes sense to others.

Hopefully, only after you've handled the 10(!!!) other AIs that you have for
homework. You, of all people, shouldn't be taking on more work without dealing
with the huge pile you already have. (And you haven't even done the relatively
easy homework, like read and comment on the AI draft I sent you back in
November and that I've repeatedly reminded you of since.)

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

From: Tucker Taft
Sent: Tuesday, May 10, 2016  8:51 PM

>> Currently, the iterator form for iterating over containers only
>> allows iteration over the elements of a container:
>>
>>    for Elem of Container loop
>>       ...
>>    end loop;
>
> This is certainly not true; the cursor form of iteration ought to be
> preferred in many circumstances (this being one of them). Indeed, the "of"
> form is a shorthand for various common cases; under no circumstances
> should it be considered the only way to do iteration.

True, but these iterators are all about raising the level of abstraction, and
cursors are too low level for many folks.  We have found almost everyone using
containers prefers the user-defined "of" iterators over those making explicit
use of cursors.

>> ... I'll write up a full AI if this general idea makes sense to others.
>
> Hopefully, only after you've handled the 10(!!!) other AIs that you
> have for homework.  ...

Fair enough!

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

From: Florian Schanda
Sent: Thursday, May 12, 2016  9:21 AM

> We have found many places where iterating over the keys of a map-like
> container is  preferable.  Hence, I would recommend we add the
> following
> syntaxes:
>
>    for (Key => Elem) of Container loop
>       ...
>    end loop;
>
> and
>
>    for (Key => <>) of Container loop
>       ...
>    end loop;

A bit fat +1 from me. I think this one of the main things that I found
irritating when using the containers ;)

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

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

> So, while the idea is sound, it barely even rises to the level of
> nice-to-have. We shouldn't be loading the next Ada up with
> nice-to-haves; we need other non-AdaCore vendors to catch up and we
> did plenty of nice-to-haves the last time around. (And this is the
> sort of thing that gives "committee-designed" languages a bad name --
> everyone's pet projects get included.)

I also note that this proposal is essentially redundant with the "lambda
loop body" proposal (see the parallel thread [Editor's note: see
AI12-0189-1]). The Map containers don't currently have the "right" iterator
for that (only having a cursor version), but it would be easy enough to add
that:

procedure Iterate
     (Container : in Map;
      Process   : not null access procedure (Key     : in Key_Type;
                                             Element : in out Element_Type));

With this iterator subprogram in the Map containers, the "lambda loop body"
proposal would give you what you want.

I'm really dubious that a separate feature is warranted for this.

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

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

> ... I'm really dubious that a separate feature is warranted for this.

I think you are in the minority on this one.  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...

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

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

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

The above is conditional on the "loop body procedures" getting defined.
There is absolutely no justification for adding *two* more ways to write
loops, with all of the implementation and definitional complexity that
entails. And I'm much more sympathetic to the "loop body procedure" scheme
given the mess that Brad went through to get the environment variable
iterators to work. (Of course that was because of the aversion people have to
"in out" parameters on functions...but too late to fix now.)

Using "loop body procedures" rather than Brad's messy iterators seems like a
win (there's complex wording either way, but the wording we'd do would be
more general), and we'd get key iterators for almost free.

We have to resist loading up Ada with every pet feature someone has (well,
except mine of course ;-). Else there will be zero implementations of
Ada 2020.

> 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...

Sounds like a plan. When are you getting back to me on my AI12-0064-2 draft?
I'd like to finish it before I leave for the meeting (and not 3 minutes
before the meeting ;-).

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

[Editor's note: This thread continues in AI12-0190-1, since it has much more
to do with general lambdas than it does with this proposal.]

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

From: Brad Moore
Sent: Saturday, June 11, 2016  9:20 PM

> Sure, but it *still* doesn't look like a loop.
[Editor's note: The above is from a thread filed in AI12-0189-1.]

The last thing I remember Jeff say before we headed back to our rooms after
dinner last night was something about having pleasant dreams about generators.
...  Thanks Jeff....

So here it is 3am, my thoughts returned to this email, and I am thinking this
is related to generators, which might be worth capturing if we are to discuss
tomorrow.

I think the Java idea could be adapted to syntax that looks more like a loop,
and it strikes me that the mechanism being used can be considered as a
generator approach.

Before I describe, first some examples of what it might look like.

The above example

   Total : Integer := 0;

    for I of Iterate(1, N).Parallel.Sum(Total) loop
       Total := Total + I;
   end loop;

Iterate(1,N) is a library call to a generator that creates a single "stream"
value that includes the start, end, and a result value (of type Integer) of
the iteration

Parallel is another library call that produces a generator and takes a
"stream" value and splits it into several substream values of "Chunks" of
iterations. The number of chunks is likely determined based on the number of
available cores. To get a sequential loop, from the above, you could just
delete the .Parallel.

Sum is an instantiation of a  "stream" collector library call that calls the
input generator produced by Parallel, to retrieve chunks, then produces in
parallel for each generator value, which is an integer. The parameter of Sum
(ie Total) is a external variable that is updated by the overall result. It
also happens to be the name of a in out parameter that is passed into a
callback that is represented by the loop body.

The loop body in fact is updating the local copy of the partial sum, and the
reduction occurs inside the Sum collector procedure, which updates the real
result.

The collector generic itself is can be a generic procedure that has two generic
formals, the identity value for the reduction, and the reducer function.

procedure Add is new Collector(Result_Type => Integer, Reduce => "+",
Identity => 0); Add could be a set of preinstantiated collectors that are
available for common use, but the programmer could create user-defined
collectors as well.

The callback probably takes also as parameters the start and end range of the
iteration, but the syntactic sugar of this loop syntax, hides that.

Now consider some other examples.

  -- Iterating through a map container by keys.
  for Pair of Container.Keys  loop
         Put_Line(Key_Type'Image(Pair.Key) & " => " &
           Elem_Type'Image(Pair.Elem));
   end loop;

Keys is a function that produces a generator for the name value pairs of the
container


  -- Iterating through a map container to produce parallel sum of elements.

  Total : Integer := 0;

  for Element of Container.Elements.Parallel.Sum(Total)  loop
         Total := Total + Element;
   end loop;

Elements is a function that produces a generator whose value is an element of
the container, rather than a name value pair.

-- Ada Environment variables.

    for Pair of Ada.Environment_Variables.Iterate loop
       Put_Line (Pair.Name & " => " & Pair.Value);
    end loop;

In this case, we'd probably need to define a Name_Value_Pair_Type and probably
a new Iterate subprogram that works with this type.


Anyway, please keep in mind it is 4am, I should probably know better to
compose emails at this time, as this might seem like a half-baked mess in the
morning. But it strikes me that this might be a nice mixture of ideas from
Tuck's loop body syntax, the generator proposal, and what Ive been thinking
of lately as an alternative.

The Java stream approach seems to be very flexible and powerful for
expression, but the Ada take on this would be better for Ada if it can make
it mechanism look more like a loop. 

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

Questions? Ask the ACAA Technical Agent