* Why does Ada.Iterator_Interfaces specify Next as a function rather than a procedure?
@ 2017-09-06 7:49 Stephen Leake
2017-09-06 21:50 ` Randy Brukardt
` (2 more replies)
0 siblings, 3 replies; 8+ messages in thread
From: Stephen Leake @ 2017-09-06 7:49 UTC (permalink / raw)
I have a fairly complex collection of stuff that I want to define an iterator for, so all clients access the stuff in the same order. The stuff is token definitions for a lexer; keywords, punctuation, whitespace, identifiers, numbers.
Initially, I defined my own Cursor type, that stores iterators to the various pieces, and manages the transitions between them.
Thus the Cursor type has state, and the "Next" subprogram is a procedure. It is _not_ just "return node.next", but more like:
if keywords not done, do Next (Keywords)
elsif punctuation not done, do Next (Punctuation)
with more logic that does First (Punctuation) at the right time. And so on for the rest of the stuff.
Then I learned about Ada 2012 Iterators (allowing things like "for Token_Name of Tokens"), so naturally I tried to define one for this collection.
It all goes well until I hit the definition of Ada.Iterator_Interfaces.Next; it must be a function, with an "in" parameter.
I can work around this with an access type, but that's just annoying. And since I would be lying to the compiler, I'm not clear what subtle bugs I might encounter. For example, the compiler might assume that it can do:
Next_Cursor := Next (Cursor);
and Next_Cursor and Cursor have different values (ie refer to different items). I can't think of why it would want to do that for a loop, but since the current definition of Next allows it, I worry about it.
If I don't use an access type, I'll have to copy the Cursor state in each call to Next, then modify it and return that. This is not in a time critical loop, so that's not really a problem, but it seems a silly waste. And my next use case could very well be in a time critical loop.
The Annotated Ada Reference Manual doesn't shed any light on this.
Would it be a problem to change Ada.Iterator_Interfaces.Next (and Previous) to take an "in out" parameter for the Cursor?
Or provide an alternate procedure Next? I guess that would have to be in a different package.
-- Stephe
(I realize I'm over five years late with this, but better late than never :)
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: Why does Ada.Iterator_Interfaces specify Next as a function rather than a procedure?
2017-09-06 7:49 Why does Ada.Iterator_Interfaces specify Next as a function rather than a procedure? Stephen Leake
@ 2017-09-06 21:50 ` Randy Brukardt
2017-09-07 1:04 ` Stephen Leake
2017-09-07 2:53 ` Charles H. Sampson
2017-09-07 6:16 ` briot.emmanuel
2 siblings, 1 reply; 8+ messages in thread
From: Randy Brukardt @ 2017-09-06 21:50 UTC (permalink / raw)
(1) The ARG is looking at alternative kinds of iterators for Ada 2020. See
AI12-0188-1 and AI12-0189-1 (probably only one of these will be used).
(2) Ada 2012 changed the rules to reflect that the Rosen trick and its
brother, the squirreling controlled type (used a lot by Claw) are legitimate
techniques, even if they cause a constant to be modified. (Essentially, true
constants are impossible for some kinds of types.)
(3) So, making the iterator type use one of these techniques, or simply have
it contain a pointer to a writable state object, should solve the problem.
(But I agree it is not optimal, thus item (1)).
That is, For Ada 2012 I'd define this something like:
type State is record
-- The components of the state.
end record;
type My_Iterator is new Ada.Finalization.Controlled and
Reversible_Iterator with record
My_State : access State; -- Writable state.
The_State : aliased State;
end record;
procedure Initialize (Obj : in out My_Iterator) is
begin
Obj.My_State := The_State'Access; -- (*)
end Initialize;
(*) This might need to be Unchecked_Access. If so, it is safe, since the
designated data will always live as long as the pointer -- they're both
parts of the same object! Unless someone copies the pointer to a global
variable -- but hopefully this is used in a private type so clients can't do
that -- in that case, only you could do something stupid. ;-)
Initialize will be called whenever any My_Iterator object is created, and
the object is always a variable at that time. Thus, you can save a writable
pointer at the time, and use it later even if you only have a constant view
of the object. This is a legitimate technique -- it is NOT erroneous in Ada
2012 -- so if your compiler has problems with it, file a bug report and if
they don't believe that, send me a proposed ACATS test!
Randy.
"Stephen Leake" <stephen_leake@stephe-leake.org> wrote in message
news:693d41af-1da7-4c47-825a-198a082aaf9a@googlegroups.com...
I have a fairly complex collection of stuff that I want to define an
iterator for, so all clients access the stuff in the same order. The stuff
is token definitions for a lexer; keywords, punctuation, whitespace,
identifiers, numbers.
Initially, I defined my own Cursor type, that stores iterators to the
various pieces, and manages the transitions between them.
Thus the Cursor type has state, and the "Next" subprogram is a procedure. It
is _not_ just "return node.next", but more like:
if keywords not done, do Next (Keywords)
elsif punctuation not done, do Next (Punctuation)
with more logic that does First (Punctuation) at the right time. And so on
for the rest of the stuff.
Then I learned about Ada 2012 Iterators (allowing things like "for
Token_Name of Tokens"), so naturally I tried to define one for this
collection.
It all goes well until I hit the definition of Ada.Iterator_Interfaces.Next;
it must be a function, with an "in" parameter.
I can work around this with an access type, but that's just annoying. And
since I would be lying to the compiler, I'm not clear what subtle bugs I
might encounter. For example, the compiler might assume that it can do:
Next_Cursor := Next (Cursor);
and Next_Cursor and Cursor have different values (ie refer to different
items). I can't think of why it would want to do that for a loop, but since
the current definition of Next allows it, I worry about it.
If I don't use an access type, I'll have to copy the Cursor state in each
call to Next, then modify it and return that. This is not in a time critical
loop, so that's not really a problem, but it seems a silly waste. And my
next use case could very well be in a time critical loop.
The Annotated Ada Reference Manual doesn't shed any light on this.
Would it be a problem to change Ada.Iterator_Interfaces.Next (and Previous)
to take an "in out" parameter for the Cursor?
Or provide an alternate procedure Next? I guess that would have to be in a
different package.
-- Stephe
(I realize I'm over five years late with this, but better late than never :)
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: Why does Ada.Iterator_Interfaces specify Next as a function rather than a procedure?
2017-09-06 21:50 ` Randy Brukardt
@ 2017-09-07 1:04 ` Stephen Leake
2017-09-07 1:29 ` Stephen Leake
0 siblings, 1 reply; 8+ messages in thread
From: Stephen Leake @ 2017-09-07 1:04 UTC (permalink / raw)
On Wednesday, September 6, 2017 at 4:50:49 PM UTC-5, Randy Brukardt wrote:
> (1) The ARG is looking at alternative kinds of iterators for Ada 2020. See
> AI12-0188-1 and AI12-0189-1 (probably only one of these will be used).
Interesting, but neither addresses my issue; the definition of Next as a function does not change.
189 mentions my problem ("often using the Rosen trick"), but seems to think the Rosen trick is an acceptable solution.
A unified cursor does make sense for my application.
> type State is record
> -- The components of the state.
> end record;
>
> type My_Iterator is new Ada.Finalization.Controlled and
> Reversible_Iterator with record
> My_State : access State; -- Writable state.
> The_State : aliased State;
> end record;
>
> procedure Initialize (Obj : in out My_Iterator) is
> begin
> Obj.My_State := The_State'Access; -- (*)
> end Initialize;
> This is a legitimate technique -- it is NOT erroneous in Ada
> 2012 -- so if your compiler has problems with it, file a bug report and if
> they don't believe that, send me a proposed ACATS test!
You are implying that my worry about the compiler assuming the input and output of Next must point to different items is not valid.
I'll give it a try.
-- Stephe
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: Why does Ada.Iterator_Interfaces specify Next as a function rather than a procedure?
2017-09-07 1:04 ` Stephen Leake
@ 2017-09-07 1:29 ` Stephen Leake
2017-09-07 23:14 ` Randy Brukardt
0 siblings, 1 reply; 8+ messages in thread
From: Stephen Leake @ 2017-09-07 1:29 UTC (permalink / raw)
On Wednesday, September 6, 2017 at 8:04:15 PM UTC-5, Stephen Leake wrote:
> > type State is record
> > -- The components of the state.
> > end record;
> >
> > type My_Iterator is new Ada.Finalization.Controlled and
> > Reversible_Iterator with record
> > My_State : access State; -- Writable state.
> > The_State : aliased State;
> > end record;
I don't see how to do this; 'Reversible_Iterator' is defined in an instantiation of Ada.Iterator_Interfaces, which takes the type My_Iterator as a generic parameter.
Just leaving that off still fails, if I try to make the contents of the cursor private while exposing the Iterate function.
Are you relying on some new Ada 202x syntax here?
> >
> > procedure Initialize (Obj : in out My_Iterator) is
> > begin
> > Obj.My_State := The_State'Access; -- (*)
> > end Initialize;
>
> > This is a legitimate technique -- it is NOT erroneous in Ada
> > 2012 -- so if your compiler has problems with it, file a bug report and if
> > they don't believe that, send me a proposed ACATS test!
>
> You are implying that my worry about the compiler assuming the input and output of Next must point to different items is not valid.
>
> I'll give it a try.
>
> -- Stephe
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: Why does Ada.Iterator_Interfaces specify Next as a function rather than a procedure?
2017-09-06 7:49 Why does Ada.Iterator_Interfaces specify Next as a function rather than a procedure? Stephen Leake
2017-09-06 21:50 ` Randy Brukardt
@ 2017-09-07 2:53 ` Charles H. Sampson
2017-09-07 6:16 ` briot.emmanuel
2 siblings, 0 replies; 8+ messages in thread
From: Charles H. Sampson @ 2017-09-07 2:53 UTC (permalink / raw)
This response ignores all the specifics of your question. Sorry.
To me this is conceptual. In answer to the question "When should a
subprogram be a function and when should it be a procedure with an out
parameter?", my answer was always, "If the primary purpose of the
subprogram is to calculate a value, it's a function, even if it does
processing other than just calculating that value. If the primary
purpose of the subprogram is to carry out an action, it's a procedure,
even if it returns a single value as part of that action."
Charlie
--
Nobody in this country got rich on his own. You built a factory--good.
But you moved your goods on roads we all paid for. You hired workers we
all paid to educate. So keep a big hunk of the money from your factory.
But take a hunk and pay it forward. Elizabeth Warren (paraphrased)
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: Why does Ada.Iterator_Interfaces specify Next as a function rather than a procedure?
2017-09-06 7:49 Why does Ada.Iterator_Interfaces specify Next as a function rather than a procedure? Stephen Leake
2017-09-06 21:50 ` Randy Brukardt
2017-09-07 2:53 ` Charles H. Sampson
@ 2017-09-07 6:16 ` briot.emmanuel
2017-09-07 23:17 ` Randy Brukardt
2 siblings, 1 reply; 8+ messages in thread
From: briot.emmanuel @ 2017-09-07 6:16 UTC (permalink / raw)
In my experience, a simpler approach is to use GNAT's Iterable aspect, which is way simpler and more logical (really just a thin layer about the usual Cursor/Has_Element/Next/... operations).
Next is still a function in that context though, so maybe it doesn't really solve your case. I am not sure why your next is a procedure. The idea is that a cursor is a relatively thin object, so light to copy around. If you have state, it should belong to an iterable.
For instance, I have used the following in GNATCOLL.Strings:
type Index_Range is record
Low, High : Natural;
end record
with Iterable => (First => First,
Next => Next,
Has_Element => Has_Element,
Element => Element);
function First (Self : Index_Range) return Positive is (Self.Low);
function Next (Self : Index_Range; Index : Positive) return Positive
is (Index + 1);
function Has_Element (Self : Index_Range; Index : Positive)
return Boolean is (Index <= Self.High);
function Element
(Self : Index_Range; Index : Positive) return Positive
is (Index);
function Iterate (Self : XString) return Index_Range
is ((Low => 1, High => Self.Length));
So from my container (XString), I get the iterable Index_Range (via Iterate) which could store some context information, and finally a Cursor. Since Next receives the iterable, that might be good enough for your case.
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: Why does Ada.Iterator_Interfaces specify Next as a function rather than a procedure?
2017-09-07 1:29 ` Stephen Leake
@ 2017-09-07 23:14 ` Randy Brukardt
0 siblings, 0 replies; 8+ messages in thread
From: Randy Brukardt @ 2017-09-07 23:14 UTC (permalink / raw)
I think you're confusing the cursor and the iterator object. (Next has two
parameters, after all.) I'm talking about the definition of the iterator
object; that's where you keep any state necessary. The cursor just provides
a reference to the current item, whatever that might be.
The instantiation takes the cursor type; it then is used to define the
actual iterator type.
In almost all cases, the iterator object has to be separate from the
underlying data structure; generally you define a function to create the
initial iterator object. (There are aspects to automate this process, look
at how the containers are structured if you want to try to use them.) One
reason for this is the possibility that multiple tasks might iterate over
the same read-only object.
Randy.
"Stephen Leake" <stephen_leake@stephe-leake.org> wrote in message
news:ba860d9f-7e5f-431b-826b-76a6347db38e@googlegroups.com...
> On Wednesday, September 6, 2017 at 8:04:15 PM UTC-5, Stephen Leake wrote:
>> > type State is record
>> > -- The components of the state.
>> > end record;
>> >
>> > type My_Iterator is new Ada.Finalization.Controlled and
>> > Reversible_Iterator with record
>> > My_State : access State; -- Writable state.
>> > The_State : aliased State;
>> > end record;
>
> I don't see how to do this; 'Reversible_Iterator' is defined in an
> instantiation of Ada.Iterator_Interfaces, which takes the type My_Iterator
> as a generic parameter.
>
> Just leaving that off still fails, if I try to make the contents of the
> cursor private while exposing the Iterate function.
>
> Are you relying on some new Ada 202x syntax here?
>> >
>> > procedure Initialize (Obj : in out My_Iterator) is
>> > begin
>> > Obj.My_State := The_State'Access; -- (*)
>> > end Initialize;
>>
>> > This is a legitimate technique -- it is NOT erroneous in Ada
>> > 2012 -- so if your compiler has problems with it, file a bug report and
>> > if
>> > they don't believe that, send me a proposed ACATS test!
>>
>> You are implying that my worry about the compiler assuming the input and
>> output of Next must point to different items is not valid.
>>
>> I'll give it a try.
>>
>> -- Stephe
>
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: Why does Ada.Iterator_Interfaces specify Next as a function rather than a procedure?
2017-09-07 6:16 ` briot.emmanuel
@ 2017-09-07 23:17 ` Randy Brukardt
0 siblings, 0 replies; 8+ messages in thread
From: Randy Brukardt @ 2017-09-07 23:17 UTC (permalink / raw)
<briot.emmanuel@gmail.com> wrote in message
news:806ff091-efd2-4625-afec-c7a6dc7886a8@googlegroups.com...
>In my experience, a simpler approach is to use GNAT's Iterable aspect,
>which
>is way simpler and more logical (really just a thin layer about the usual
>Cursor/Has_Element/Next/... operations).
If he's trying to create portable Ada code, that's hardly an option. Perhaps
Ada 202x will include an easier solution to the problem.
I tend to agree that the use of interfaces here was a mistake; we ended up
needing a bunch of aspects to make the solution usable, and at that point we
should have just abandoned the interfaces and just used aspects. (This is
one of my reasons for saying that interfaces themselves are pointless.) In
any case, though, portable Ada code is stuck with them.
Randy.
^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2017-09-07 23:17 UTC | newest]
Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-09-06 7:49 Why does Ada.Iterator_Interfaces specify Next as a function rather than a procedure? Stephen Leake
2017-09-06 21:50 ` Randy Brukardt
2017-09-07 1:04 ` Stephen Leake
2017-09-07 1:29 ` Stephen Leake
2017-09-07 23:14 ` Randy Brukardt
2017-09-07 2:53 ` Charles H. Sampson
2017-09-07 6:16 ` briot.emmanuel
2017-09-07 23:17 ` Randy Brukardt
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox