comp.lang.ada
 help / color / mirror / Atom feed
* 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