I'm following up on my previous response, as I hadn't fully grasped the power
of the procedural iterators.
To me, they appear similar to lambda expressions and closures in other
languages.
I really like the power offered by the procedural iterators.
When we call, for example:
for (Name, Val) of Ada.Environment_Variables.Iterate(<>) loop
Put_Line (Name & " => " & Val);
end loop;
It appears that we don't need guarantee that "
Ada.Environment_Variables.Iterate" actually does any iterating;
We're just giving it the content of the procedure that it may call in an
iteration, or use however it likes.
In the Environment_Variables example, the fact that there's a conceptual
container involved where iterating may happen is completely encapsulated.
It uses the Allows_Exit aspect, but it doesn't need to.
There's a procedure that happens to be called iterate, that takes access to a
procedure, and we just build the procedure as though it were the body of a loop.
That's really neat. I like that.
Nothing in the Procedural Iterators section says anything about a "for in"
loop, though, because there's no real concept of a Key or index.
For environment variables, the name is effectively a key, but you can't
actually tell from the spec, aside from human interpretation.
That's an implementation detail. In reality, Name and Value are just two
parameters of a procedure that the "Iterate" procedure may call zero to many
times.
"For of" makes sense. "For in" doesn't really apply.
I think I'm back to my original answer, though:
If we're at a place in code where we have access to the concept of a container
with keys and values,
we can use "for in" to get a cursor that can give us the key and the value.
We can write:
for Cursor in My_Obj.Iterate loop
Do_Something_With (Container_Pkg.Key (Cursor), Container_Pkg.Value
(Cursor));
end loop;
If we don't have access to the fact that we have a container, then we can't
magically manufacture cursors, keys, and values.
For the environment variables example, the Iterate procedure provides the name
and the value to procedure it calls.
For Ordered_Maps, the Iterate function returns a Reversible_Iterator'Class.
If we wanted to use a procedural iterator with, for example, an instance of
ordered maps, we can do so with the other existing "Iterate" procedure profiled
like this:
procedure Iterate
(Container : Map;
Process : not null access procedure (Position : Cursor));
for (Cursor) of My_Map.Iterate (<>) loop
Do_Something_With (Container_Pkg.Key (Cursor), Container_Pkg.Value
(Cursor));
end loop;
In fact, that's equivalent to the example that is already there:
for (C : Cursor) of My_Map.Iterate loop
Put_Line (My_Key_Type'Image (Key (C)) & " => " & My_Element_Type'Image
(Element (C)));
end loop;
if, for example, the ordered maps container had the following procedure (which
it doesn't, but could):
procedure Iterate
(Container : Map;
Process : not null access procedure (Key : Key_Type; Value :
Element_Type));
Then we could do the following:
for (Key, Value) of My_Map.Iterate (<>) loop
Do_Something_With (Key, Value);
end loop;
or equivalently:
for (Key, Value) of My_Map.Iterate loop
Put_Line (My_Key_Type'Image (Key) & " => " & My_Element_Type'Image
(Element));
end loop;
This doesn't seem all that different from the existing case with a cursor.
Given that ordered maps are a visible container with an iterator, and Key Value
functions provided, none of this seems necessary for that particular case,
However, being able to use procedural iterators on objects as illustrated
above, will be useful when the other mechanisms for iterating are encapsulated.
Suppose, for example, someone defined a package like Ada.Environment_Variables,
called Environment_Objects.
The spec is identical, except that all the subprograms call into a tagged
object as their first parameter.
You can query Value, and Exists, and call Set and Clear on the object type
defined in the package.
The object type could be implemented under-the-hood with an indefinite ordered
map of strings, but you can't tell from the spec.
From the spec, there's no iterator type or cursor defined, but there is an
iterate procedure:
procedure Iterate (Env : Object; Process : not null access procedure (Name,
Value : String));
for such a package, I believe we could call the following:
for (Name, Val) of Environment_Objects.Iterate(My_Object, <>) loop
Put_Line (Name & " => " & Val);
end loop;
That being the case, the procedural iterators definition seems to cover the
cases for what it is.
The ability to get both key and value in a loop from a publically iterable type
would be an extension to generalized loop iteration.
We have "for in" and "for of", this would give us another kind of "for of",
distinct from procedural iterators.
Unfortunately, that it is potentially confusing with the procedural iterators
also available with similar syntax.
Joshua Fletcher
613-721-4405
-----Original Message-----
From: Fletcher, Joshua
Sent: Monday, December 9, 2019 12:07 PM
To: Ada-Comment List <[email protected]>
Subject: RE: [Ada-Comment] Possible generalization of Procedural Iterator
This proposal doesn't seem like a fair comparison:
It presents a new form of the 'for of' syntax that provides the key and the
value in the loop, and contrasts it with using a generic to manage the loop.
The more accurate contrast would be with a 'for in' loop - especially the Ada
2012 kind, which can provide access to the iterator (or Cursor) in the loop.
The existing syntax that someone might use to contrast that with is:
for Cursor in My_Obj.Iterate loop
Do_Something_With (Container_Pkg.Key (Cursor), Container_Pkg.Value
(Cursor));
end loop;
(You could also write Do_Something to take a Cursor, depending on its
relationship to the container type)
Note: Here, the Iterate procedure returns an Interator from
Ada.Iterator_Interfaces, and the Cursor type has a Key and Value function.
Not all Cursor types have both, since a key isn't relevant for all iterate-able
structures; Doubly linked lists don't have a meaningful key, and Vectors have a
"To_Index" function instead of a Key function.
A structure comparable two a multi-dimensional array could have multiple keys;
but that's not much different from a key with multiple elements.
I'm not saying it's a bad idea - it would give the 'best of both worlds' of a
"for in" and a "for of" loop in one.
- "For in" gives the cursor (which typically has accessors for key and value),
"For of" just gives the value without the key.) I prefer "for of" loops for any
case where I don't actually need the key (or index). If I need the key (or
index), I use "for in"
Tucker's suggested new syntax would make it easier to get the key and value
inside the loop (eliminating the step of having to extract them from the
Cursor), but the existing cost of doing so is pretty small because of what was
added in Ada 2012, and doesn't typically require a generic.
Joshua Fletcher
613-721-4405
-----Original Message-----
From: Tucker Taft @ adacore <[email protected]>
Sent: Tuesday, December 3, 2019 6:19 PM
To: Ada-Comment List <[email protected]>
Subject: [Ada-Comment] Possible generalization of Procedural Iterator
While putting together some slides about the "procedural iterator"
(https://smex-ctp.trendmicro.com:443/wis/clicktime/v1/query?url=http%3a%2f%2fwww.ada%2dauth.org%2fstandards%2f2xaarm%2fhtml%2fAA%2d5%2d5%2d3.html&umid=0ba83771-1871-4370-99cd-3ddf9200b2b6&auth=eb73852e237f4c399a45f8ab019f4a6dae168ea9-19b9591f9899b75811cd3682211a7403b49e9b92)
I realized that there is a very similar case which comes up very often, at
least in my code, which might also benefit from the procedural iterator
syntactic sugar. In particular, I have many, many instances of generic
(iterating) procedures that have exactly one generic formal parameter, which is
a formal procedure. For example:
generic
with procedure Action (Key : Key_Type; Value : Value_Type);
procedure Iterate_Some_Structure (Obj : Structure_Type);
A typical usage is as follows:
declare
procedure Process_One (Key : Key_Type; Value : Value_Type) is
begin
Do_Something_With (Key, Value);
end Process_One;
procedure Process_All is new Iterate_Some_Structure (Process_One);
begin
Process_All (My_Obj);
end;
This is quite similar to the situation of calling an iterating procedure that
has a single access-to-procedure parameter, plus zero or more other parameters.
So perhaps the same syntactic sugar should apply. That is:
for (Key, Value) of Iterate_Some_Structure (My_Obj) loop
Do_Something_With (Key, Value);
end loop;
I would claim that the "sugared" version is much easier to read and understand
than the gobbledygook above it. And at least from my experience, I have many
places where I could use this second sort of procedural iterator. One
interesting aspect of this second sugaring is that the parameters are already
separated into the formal procedure and the other parameters, so there is no
need for a "<>" placeholder.
Comments?
-Tuck
________________________________________________________
You have received this message because you subscribed to the Ada-Comment
mailing list. To leave the Ada-Comment list, send an email with 'leave
Ada-Comment' in the body to [email protected]. For help on the other
commands available, send 'help Ada-Comment' to the same address.
Problems? Send mail to [email protected]. This list is operated by the Ada
Resource Association, Inc., PO Box 8685, New York NY 10116-8685.
“This message and/or attachments may include information subject to GD
Corporate Policies 07-103 and 07-105 and is intended to be accessed only by
authorized recipients. Use, storage and transmission are governed by General
Dynamics and its policies. Contractual restrictions apply to third parties.
Recipients should refer to the policies or contract to determine proper
handling. Unauthorized review, use, disclosure or distribution is prohibited.
If you are not an intended recipient, please contact the sender and destroy all
copies of the original message.”