CVS difference for ai05s/ai05-0139-2.txt
--- ai05s/ai05-0139-2.txt 2010/02/16 03:18:00 1.4
+++ ai05s/ai05-0139-2.txt 2010/02/23 07:31:06 1.5
@@ -20,10 +20,10 @@
A better mechanism is required.
-In addition, using the notion of "accessors" or "references" as defined in AI05-0142
-is syntactically awkward. Finally, it is often clearer to iterate over the elements
-of a container (or array) directly, rather than iterating over a cursor (or index)
-into the container (or array).
+In addition, using the notion of "accessors" or "references" as defined in
+AI05-0142 is syntactically awkward. Finally, it is often clearer to iterate over
+the elements of a container (or array) directly, rather than iterating over a
+cursor (or index) into the container (or array).
Using accessors for user-defined dereference (rather than simple access values
as proposed in AI05-141) has the potential to provide a safer mechanism that
@@ -1433,5 +1433,1938 @@
overkill to try to get it to work for containers as well.
As usual, any and all comments welcome.
+
+****************************************************************
+
+From: Tucker Taft
+Sent: Thursday, February 11, 2010 7:19 AM
+
+I didn't like the "default iterator" solution in version 3, so here is version 4
+which uses the specification of two aspects, Default_Iterator and
+Iterator_Element, rather than the signature generic. This makes the solution
+more palatable to me. I recommend you ignore version 3.
+
+****************************************************************
+
+From: Randy Brukardt
+Sent: Thursday, February 11, 2010 10:17 PM
+
+[The first of several free association thoughts on this proposal.]
+
+It would be appealing to define:
+
+ function "()" (S : Unbounded_String; I : Positive) renames Element;
+
+but unfortunately Unbounded_String is not tagged, so this doesn't work.
+
+****************************************************************
+
+From: Randy Brukardt
+Sent: Thursday, February 11, 2010 10:23 PM
+
+> One relatively important rule for "()" is that one can define two
+> operators, one that requires the controlling operand to be a variable,
+> and one that allows it to be a constant, and overload resolution will
+> resolve to the one that requires a variable if the actual is a
+> variable, and otherwise it will resolve to the one that allows a
+> constant. This rule is important so that the same notation, such as
+> "Container(Key)", can be used when Container is a constant as when it
+> is a variable, and the variableness of the result can be determined by
+> the variableness of the Container.
+
+Unfortunately, this is purely fantasy. Resolution rules do not take anything
+about constant vs. variable into account -- those are legality rules. Even if it
+wasn't fantasy, it still wouldn't make sense, since a variable can always be
+passed where a constant is allowed. It would seem odd to eliminate that.
+
+This seems like a downside of any proposal to eliminate the function names from
+calls. (Unless you are proposing a new kind of resolution to make this work; it
+would in fact make sense in this case, but I have to wonder about a preference
+rule since they're usually problematic.)
+
+****************************************************************
+
+From: Randy Brukardt
+Sent: Thursday, February 11, 2010 10:38 PM
+
+In your example, you have the following:
+
+> function "()"(Tab : access T_Table; Key : String) return Ref_T;
+
+This works OK, but it is important to note that it isn't safe, in that the
+access value returned as the discriminant of Ref_T can outlive the Ref_T object
+(at least if I understand AI-51's model, which perhaps I don't in some subtle
+case).
+
+The "aliased" parameters of AI05-0142-4 were designed to eliminate that problem
+(and the need for dynamic accessibility checking). In that case, the
+specification would look like:
+
+ function "()"(Tab : aliased in out T_Table; Key : String) return Ref_T;
+
+I couldn't tell whether you were proposing that the original formulation was
+enough in all cases (which didn't seem to be the case last year, and I doubt
+that's changed), or whether you were just avoiding the AI-142-4 and AI-143
+features necessary to make the intended formulation work in the interest of
+avoiding mixing of a bunch of new features.
+
+****************************************************************
+
+From: Tucker Taft
+Sent: Friday, February 12, 2010 6:34 AM
+
+I agree that "aliased in out" is preferable to "access"
+but I didn't want to tie the two AIs too closely at this point. I don't believe
+there is a safety issue. If there is, then AI-51 needs to be fixed. I am
+certainly hoping we are going to tackle AI-51 at this meeting!
+
+****************************************************************
+
+From: Bob Duff
+Sent: Friday, February 12, 2010 12:57 PM
+
+This syntactic sugar is really great! I've been wanting things like this for a
+long time.
+
+Can we add user-defined literals to this AI, so unbounded strings can be
+tolerable?
+
+I'm disappointed that the "of" iterators require the cursor stuff. It would be
+much cleaner to define them in terms of things like:
+
+ procedure Iterate
+ (Container : in Vector;
+ Process : not null access procedure (Position : in Cursor));
+
+But I don't see how that can work, because then you can't "exit".
+Sigh.
+
+...
+> Using an iterator over one of the Ada 2005 containers libraries
+> requires writing a subprogram which gets called repeatedly with each element of the container.
+> That effectively requires writing the loop body out-of-line from the
+> loop, making the code inside-out of its normal flow, requiring the
+> writing of a lot of extra text, and thus making it much harder to read and understand.
+
+Not to mention having to give it a meaningless name.
+
+...
+> type Ref_T(Data : access T) is
+> new Ada.Finalization.Limited_Controlled
+> and Ada.References.Reference with ...
+
+Why not "null record"?
+
+...
+> package body Whatever is
+> function Element(Tab : access T_Table; Key : String) return Ref_T is
+> -- Return a "reference" to an element of the table
+> begin
+> -- "Wrap" the pointer in a reference object
+> return Ref_T'(Data => Lookup(Tab, Key));
+
+Is Lookup_Or_Create meant?
+
+...
+> package Whatever is
+> ... as above
+>
+> function "()"(Tab : access T_Table; Key : String) return Ref_T;
+
+Here and below, id the "access" significant? Could it have been "in out"?
+
+...
+> The basic rule with "()" is that it must be suitable for prefix
+> notation, meaning that it is either a primitive operation or a
+> class-wide operation of some tagged type (via its first parameter),
+> and if class-wide, is declared immediately within a package where some
+> tagged type covered by the class-wide type is immediately declared.
+> It must have at least two parameters. There seems no particular
+> reason it couldn't have more than two parameters. There are no predefined "()"
+> operators, though one could imagine that all array types have such
+> operators, if it helps in understanding the model.
+
+It seems annoying to restrict it to tagged types.
+Is that really necessary?
+
+I see below that cursor types will have to be tagged to take advantage of this
+notation. That's even more annoying, because cursors can often be quite
+lightweight.
+
+> One relatively important rule for "()" is that one can define two
+> operators, one that requires the controlling operand to be a variable,
+> and one that allows it to be a constant, and overload resolution will
+> resolve to the one that requires a variable if the actual is a
+> variable, and otherwise it will resolve to the one that allows a
+> constant. This rule is important so that the same notation, such as
+> "Container(Key)", can be used when Container is a constant as when it
+> is a variable, and the variableness of the result can be determined by
+> the variableness of the Container.
+
+Well, that's a radical notion! I see that it's necessary.
+
+Do you mean "allows a constant" or "requires a constant"?
+If the former, I think it introduces a Beaujolias effect.
+
+Would it make any sense to instead resolve on the basis of whether the context
+requires a variable? So for "X(C) := X(C) + 1;" the first would resolve to the
+variable one, and the second to the constant one.
+
+...
+> What the above presumes is that the function Iterate returns an object
+> of a type derived from a "magic" iterator type (see below). The
+> iterator type provides a First and Next operation which return a
+> Cursor type, and a No_Element cursor value to indicate end of
+> iteration. The above expands into:
+>
+> declare
+> _Iter : Iterator_Type := Iterate(Container);
+> Cursor : Cursor_Type := First(_Iter);
+> begin
+> while Cursor /= No_Element loop
+
+Are you sure you want to require every cursor type to have a special No_Element
+value?
+
+...
+> Note that the routines defined in this magic generic generally match
+> those used in iteration for the Ada.Containers packages, and their
+> meaning is the same. The only difference is that we've added an
+> iterator object parameter to the Next and Previous functions (which is
+> necessary to make them part of the interface).
+
+Are we going to add all this stuff to the existing container packages?
+
+...
+> This second syntax is more convenient, but less flexible, in that
+> there is no way to specify the particular iterator to use, so it
+> requires that there be a "default" iterator.
+
+Only for containers -- not arrays.
+
+...
+> The second requires an expression returning an array, or an object of
+> a type which has aspects Default_Iterator and Iterator_Element
+> specified. The subtype_indication is optional, since the
+> Iterator_Element aspect of the container type determines the Element
+> type. Similarly for an array, the Element type is simply the array
+> component type. One purpose of the subtype_indication might be to
+> tighten the requirements on the elements of the container/array,
+> acting essentially as an assertion about the values stored in the
+> container/array at the time of the iteration. Another purpose is
+> simply to help the reader, and provide an extra check that the
+> Iterator_Element type of the container is as expected.
+
+Hmm. I'm inclined to always use a subtype_indication, for readability. Should we require that?
+If so, we can change "OF" to "IN" in the syntax, which seems a slight improvement.
+
+...
+> Vec : Int_Vectors.Vector;
+> ...
+> for X of Vec loop
+> -- X renames Vec(<cursor>) where <cursor> in Default_Iterator(Vec).
+> -- and Vec(<cursor>) is equiv to "()"(Vec, <cursor>).Discrim.all.
+> -- X is a variable if Vec is a variable.
+>
+> X := X + 1;
+> end loop;
+>
+> The "of" syntax, suggested by J.P. Rosen, seems to help identify X as
+> an element *of* the array/container rather than as an index or cursor
+> *in* the sequence defined by the discrete range or iterator.
+
+For some reason, "of" rubs me the wrong way. The elements are "in" the
+container, just as much as indices are "in" a range.
+
+I didn't read further, since you said you hadn't updated the rest.
+
+****************************************************************
+
+From: Tucker Taft
+Sent: Friday, February 12, 2010 2:32 PM
+
+...
+> Can we add user-defined literals to this AI, so unbounded strings can
+> be tolerable?
+
+You don't like the "+" operator for this?
+
+I suppose we could define another new operator for something like this, or an
+aspect/attribute. If you have something in mind, go ahead and suggest it. I'm
+pretty content with using "+".
+
+> I'm disappointed that the "of" iterators require the cursor stuff. It
+> would be much cleaner to define them in terms of things like:
+>
+> procedure Iterate
+> (Container : in Vector;
+> Process : not null access procedure (Position : in Cursor));
+>
+> But I don't see how that can work, because then you can't "exit".
+> Sigh.
+
+You could have it take a function rather than a procedure, and return True or
+False to select "exit" or "continue".
+
+But you can also exit a loop prematurely using a "goto", a "return", a
+"requeue", an "exit" to an outer loop, etc. You put that all together and it
+creates a need to return a "continuation," and I think that is a bit too
+radical, even for Ada 2012. ;-)
+
+> ...
+>> with Ada.References;
+>> with Ada.Finalization;
+>> package Whatever is
+>> type T is private;
+>> Null_T : constant T; -- default initial value for T
+>> function New_T(...) return T;
+>>
+>> type T_Table is tagged limited private;
+>>
+>> type Ref_T(Data : access T) is
+>> new Ada.Finalization.Limited_Controlled
+>> and Ada.References.Reference with ...
+>
+> Why not "null record"?
+
+That would probably work in many cases.
+It depends on what sort of cleanup action is required when the reference goes
+away.
+
+...
+>> package body Whatever is
+>> function Element(Tab : access T_Table; Key : String) return Ref_T is
+>> -- Return a "reference" to an element of the table
+>> begin
+>> -- "Wrap" the pointer in a reference object
+>> return Ref_T'(Data => Lookup(Tab, Key));
+>
+> Is Lookup_Or_Create meant?
+
+Oops, yes I meant Lookup_Or_Create.
+
+
+>> package Whatever is
+>> ... as above
+>>
+>> function "()"(Tab : access T_Table; Key : String) return
+>> Ref_T;
+>
+> Here and below, id the "access" significant? Could it have been "in
+> out"?
+
+I think the param might need to also be "aliased", and I didn't want to tie this
+too closely to the "aliased" parameter mode AI.
+
+>> The basic rule with "()" is that it must be suitable for prefix
+>> notation, meaning that it is either a primitive operation or a
+>> class-wide operation of some tagged type (via its first parameter),
+>> and if class-wide, is declared immediately within a package where
+>> some tagged type covered by the class-wide type is immediately
+>> declared. It must have at least two parameters. There seems no
+>> particular reason it couldn't have more than two parameters. There are no predefined "()"
+>> operators, though one could imagine that all array types have such
+>> operators, if it helps in understanding the model.
+>
+> It seems annoying to restrict it to tagged types.
+> Is that really necessary?
+
+Probably not absolutely necessary. Since we only have prefix notation for
+tagged types, it seemed natural to piggy-back on that. But these are really
+pretty different, I suppose, so we could consider defining these for untagged
+types.
+
+> I see below that cursor types will have to be tagged to take advantage
+> of this notation. That's even more annoying, because cursors can
+> often be quite lightweight.
+
+I didn't mean for cursors to have to be tagged.
+Where does that requirement come in? It was not intentional.
+
+>> One relatively important rule for "()" is that one can define two
+>> operators, one that requires the controlling operand to be a
+>> variable, and one that allows it to be a constant, and overload
+>> resolution will resolve to the one that requires a variable if the
+>> actual is a variable, and otherwise it will resolve to the one that
+>> allows a constant. This rule is important so that the same notation,
+>> such as "Container(Key)", can be used when Container is a constant as
+>> when it is a variable, and the variableness of the result can be
+>> determined by the variableness of the Container.
+>
+> Well, that's a radical notion! I see that it's necessary.
+>
+> Do you mean "allows a constant" or "requires a constant"?
+> If the former, I think it introduces a Beaujolias effect.
+>
+> Would it make any sense to instead resolve on the basis of whether the
+> context requires a variable?
+> So for "X(C) := X(C) + 1;" the first would resolve to the variable
+> one, and the second to the constant one.
+
+That might be better. I know this concerned Randy as well.
+I doubt if there is a Beaujolais effect, because this preference would
+presumably only apply to operators declared in the same scope. If the
+preference might affect resolution across packages, that would definitely be an
+issue.
+
+I guess one important issue is whether this is a visibility issue at all. This
+is more like the prefix-notation than like a visibility lookup. So I see it
+more as "selecting" the right "()" operator rather than using normal overload
+resolution. Clearly the devil will be in the wording for this one, if we decide
+we want this functionality.
+
+> ...
+>> for Element of Arr loop
+>> Element := Element + 1;
+>> end loop;
+>>
+>> This second syntax is more convenient, but less flexible, in that
+>> there is no way to specify the particular iterator to use, so it
+>> requires that there be a "default" iterator.
+>
+> Only for containers -- not arrays.
+
+Correct.
+
+> ... The subtype_indication is optional, since the Iterator_Element
+>> aspect of the container type determines the Element type. Similarly
+>> for an array, the Element type is simply the array component type.
+>> One purpose of the subtype_indication might be to tighten the
+>> requirements on the elements of the container/array, acting
+>> essentially as an assertion about the values stored in the
+>> container/array at the time of the iteration. Another purpose is
+>> simply to help the reader, and provide an extra check that the
+>> Iterator_Element type of the container is as expected.
+>
+> Hmm. I'm inclined to always use a subtype_indication, for
+> readability. Should we require that?
+> If so, we can change "OF" to "IN" in the syntax, which seems a slight
+> improvement.
+
+I don't agree. Making the presence of a subtype_indication completely change
+the meaning seems weird to me. I think we want the syntax distinction as well.
+
+...
+>> The "of" syntax, suggested by J.P. Rosen, seems to help identify X as
+>> an element *of* the array/container rather than as an index or cursor
+>> *in* the sequence defined by the discrete range or iterator.
+>
+> For some reason, "of" rubs me the wrong way. The elements are "in"
+> the container, just as much as indices are "in" a range.
+
+I think you naturally talk about the elements "of" a container, at least as
+often as you talk about the elements "in" a container. For arrays, we talk about
+an array "of" integers, not an array "containing" integers. I realize that is
+using "of" in the opposite sense, but "of" is a more symmetrical preposition
+than "in" in any case. Anne of Green Gables. Green Gables of Anne. Both make
+sense. ;-)
+
+> I didn't read further, since you said you hadn't updated the rest.
+
+Right-O. I actually tried to delete what wasn't applicable anymore, so the
+discussion and example sections are still worth reading, but they aren't
+complete at all.
+
+In any case, thanks for your careful reading of the new proposal section.
+
+****************************************************************
+
+From: Bob Duff
+Sent: Friday, February 12, 2010 3:28 PM
+
+> > Can we add user-defined literals to this AI, so unbounded strings
+> > can be tolerable?
+>
+> You don't like the "+" operator for this?
+
+I think it's an ugly hack. But I do tolerate it.
+
+It's kind of annoying that this is a syntax error:
+
+ X : Unbounded_String := +"Hello";
+ Y : Unbounded_String := X & +", world.";
+
+> I suppose we could define another new operator for something like
+> this, or an aspect/attribute.
+> If you have something in mind, go ahead and suggest it. I'm pretty
+> content with using "+".
+
+I don't like the "new operator" idea. That's just a different-looking but equally-ugly hack.
+
+My idea would be to have an aspect like
+Literal_Value that's called implicitly, so:
+
+ X : T := "Hello";
+ Y : T2 := 123;
+
+is equivalent to
+
+ X : T := T'Literal_Value("Hello");
+ Y : T2 := T'Literal_Value("123");
+
+This AI is all about syntactic sugar, so I thought maybe I could sneak this in. ;-)
+
+> > I'm disappointed that the "of" iterators require the cursor stuff.
+> > It would be much cleaner to define them in terms of things like:
+> >
+> > procedure Iterate
+> > (Container : in Vector;
+> > Process : not null access procedure (Position : in Cursor));
+> >
+> > But I don't see how that can work, because then you can't "exit".
+> > Sigh.
+>
+> You could have it take a function rather than a procedure, and return
+> True or False to select "exit" or "continue".
+
+So an "exit" in the loop would turn into "return True"?
+Hmm. Maybe. Doesn't work for nested loops.
+
+> But you can also exit a loop prematurely using a "goto", a "return", a
+> "requeue", an "exit" to an outer loop, etc.
+> You put that all together and it creates a need to return a
+> "continuation," and I think that is a bit too radical, even for Ada
+> 2012. ;-)
+
+Right, that's what I meant by "But I don't see how that can work".
+
+You just need coroutines, not the full mind-bending power of continuations, I
+think.
+
+It's disappointing, though, because it's often easier (and never harder) to
+write an iterator that way. Think about an iterator that's a recursive tree
+walk. To implement the get-next-item style, you have to horse around with an
+explicit stack that is guaranteed to be both too small and too big (or too
+inefficient).
+
+> > It seems annoying to restrict it to tagged types.
+> > Is that really necessary?
+>
+> Probably not absolutely necessary. Since we only have prefix notation
+> for tagged types, it seemed natural to piggy-back on that. But these
+> are really pretty different, I suppose, so we could consider defining
+> these for untagged types.
+
+OK, good. Let's try to do that.
+
+> >
+> > I see below that cursor types will have to be tagged to take
+> > advantage of this notation. That's even more annoying, because
+> > cursors can often be quite lightweight.
+>
+> I didn't mean for cursors to have to be tagged.
+> Where does that requirement come in? It was not intentional.
+
+They have to be tagged if you want to use "()", which I think is common, isn't
+it?
+
+> > Do you mean "allows a constant" or "requires a constant"?
+> > If the former, I think it introduces a Beaujolias effect.
+> >
+> > Would it make any sense to instead resolve on the basis of whether
+> > the context requires a variable?
+> > So for "X(C) := X(C) + 1;" the first would resolve to the variable
+> > one, and the second to the constant one.
+>
+> That might be better.
+
+But it doesn't work if you rename X(C), right?
+
+>...I know this concerned Randy as well.
+> I doubt if there is a Beaujolais effect, because this preference
+>would presumably only apply to operators declared in the same scope.
+>If the preference might affect resolution across packages, that would
+>definitely be an issue.
+
+It's still a Beaujolais effect if it's within the same package. You could argue
+that such Beaujolais effects are not so bad.
+
+I'm thinking that you have just the constant one.
+X is a variable, and you say "Put(X(C));".
+Then you add the variable version of "()", and now it silently switches to that
+one (under one interpretation of your words).
+
+> I guess one important issue is whether this is a visibility issue at
+> all. This is more like the prefix-notation than like a visibility
+> lookup. So I see it more as "selecting" the right "()" operator
+> rather than using normal overload resolution. Clearly the devil will
+> be in the wording for this one, if we decide we want this
+> functionality.
+
+Seems like we need this functionality, or the whole feature is rather crippled.
+
+> > Hmm. I'm inclined to always use a subtype_indication, for
+> > readability. Should we require that?
+> > If so, we can change "OF" to "IN" in the syntax, which seems a
+> > slight improvement.
+>
+> I don't agree. Making the presence of a subtype_indication completely
+> change the meaning seems weird to me.
+> I think we want the syntax distinction as well.
+
+A matter of taste, I suppose. (But ": subtype_indication"
+is syntax!)
+
+We could still consider requiring the subtype_indication and keep "of". What do
+you think? I'm not sure. Maybe there are cases where the type is so obvious you
+want to leave it out.
+
+> I think you naturally talk about the elements "of" a container, at
+> least as often as you talk about the elements "in" a container.
+> For arrays, we talk about an array "of" integers, not an array
+> "containing" integers. I realize that is using "of" in the opposite
+> sense, but "of" is a more symmetrical preposition than "in" in any
+> case. Anne of Green Gables. Green Gables of Anne.
+> Both make sense. ;-)
+
+Well, if nobody agrees with me, I'll give in.
+It's not important. (Note that I still sometimes type "case X of^H^His" N years
+after leaving the Pascal world for Ada. ;-))
+
+****************************************************************
+
+From: Bob Duff
+Sent: Friday, February 12, 2010 3:38 PM
+
+> But you can also exit a loop prematurely using a "goto", a "return", a
+> "requeue", an "exit" to an outer loop, etc.
+> You put that all together and it creates a need to return a
+> "continuation," and I think that is a bit too radical, even for Ada
+> 2012. ;-)
+
+Actually, all you really need is an implementation of all those goto-like things
+in terms of exceptions.
+
+By the way, the most elegant iterators I've ever seen are in Sather, which is a
+little-known follow-on to Eiffel. (I think Sather is the name of a tower near
+Stanford U. Get it?) A little too elegant, in fact.
+
+****************************************************************
+
+From: Tucker Taft
+Sent: Friday, February 12, 2010 3:54 PM
+
+>> You don't like the "+" operator for this?
+>
+> I think it's an ugly hack. But I do tolerate it.
+>
+> It's kind of annoying that this is a syntax error:
+>
+> X : Unbounded_String := +"Hello";
+> Y : Unbounded_String := X & +", world.";
+
+You presumably don't need to use "+" for an operand of "&" since you can
+overload that directly on String.
+
+>
+>> I suppose we could define another new operator for something like
+>> this, or an aspect/attribute.
+>> If you have something in mind, go ahead and suggest it. I'm pretty
+>> content with using "+".
+>
+> I don't like the "new operator" idea. That's just a different-looking
+> but equally-ugly hack.
+>
+> My idea would be to have an aspect like Literal_Value that's called
+> implicitly, so:
+>
+> X : T := "Hello";
+> Y : T2 := 123;
+>
+> is equivalent to
+>
+> X : T := T'Literal_Value("Hello");
+> Y : T2 := T'Literal_Value("123");
+>
+> This AI is all about syntactic sugar, so I thought maybe I could sneak
+> this in. ;-)
+
+With a single Literal_Value aspect/attribute, it seems hard to know which of the
+following should be legal for a given type:
+
+ Y : T2 := "123";
+or
+ Y : T2 := 123;
+
+You might find my ideas about this in my pie-in-the-sky language "ParaSail"
+interesting:
+
+http://parasail-programming-language.blogspot.com/2009/12/parasail-character-string-and-numeric.html
+
+and
+
+http://parasail-programming-language.blogspot.com/2009/12/parasail-universal-types-in-annotations.html
+
+These rely on being able to specify universal types explicitly in the
+declarations of certain operators. But perhaps something analogous could be
+done in Ada.
+
+> ...
+>>> It seems annoying to restrict it to tagged types.
+>>> Is that really necessary?
+>> Probably not absolutely necessary. Since we only have prefix
+>> notation for tagged types, it seemed natural to piggy-back on that.
+>> But these are really pretty different, I suppose, so we could
+>> consider defining these for untagged types.
+>
+> OK, good. Let's try to do that.
+>
+>>> I see below that cursor types will have to be tagged to take
+>>> advantage of this notation. That's even more annoying, because
+>>> cursors can often be quite lightweight.
+>> I didn't mean for cursors to have to be tagged.
+>> Where does that requirement come in? It was not intentional.
+>
+> They have to be tagged if you want to use "()", which I think is
+> common, isn't it?
+
+Only the container needed to be tagged, not the "index" type (which is where the
+Cursor fits in). If I stated otherwise, that wasn't my intent.
+
+>>> Do you mean "allows a constant" or "requires a constant"?
+>>> If the former, I think it introduces a Beaujolias effect.
+>>>
+>>> Would it make any sense to instead resolve on the basis of whether
+>>> the context requires a variable?
+>>> So for "X(C) := X(C) + 1;" the first would resolve to the variable
+>>> one, and the second to the constant one.
+>> That might be better.
+>
+> But it doesn't work if you rename X(C), right?
+
+Yes, in a rename you would clearly want to make it a variable if C were a
+variable, so you are back to looking at the variableness of C, so you might as
+well always use that as the determining factor.
+
+>> ...I know this concerned Randy as well.
+>> I doubt if there is a Beaujolais effect, because this preference
+>> would presumably only apply to operators declared in the same scope.
+>> If the preference might affect resolution across packages, that would
+>> definitely be an issue.
+>
+> It's still a Beaujolais effect if it's within the same package. You
+> could argue that such Beaujolais effects are not so bad.
+
+I don't follow that. You'll have to show me an example.
+
+> I'm thinking that you have just the constant one.
+> X is a variable, and you say "Put(X(C));".
+> Then you add the variable version of "()", and now it silently
+> switches to that one (under one interpretation of your words).
+
+Oh, I see. That hardly seems like a maintenance issue. That almost seems more
+like a feature.
+
+...
+>>> Hmm. I'm inclined to always use a subtype_indication, for
+>>> readability. Should we require that?
+>>> If so, we can change "OF" to "IN" in the syntax, which seems a
+>>> slight improvement.
+>> I don't agree. Making the presence of a subtype_indication
+>> completely change the meaning seems weird to me.
+>> I think we want the syntax distinction as well.
+>
+> A matter of taste, I suppose. (But ": subtype_indication"
+> is syntax!)
+>
+> We could still consider requiring the subtype_indication and keep
+> "of". What do you think? I'm not sure.
+> Maybe there are cases where the type is so obvious you want to leave
+> it out.
+
+For an array it would be annoying I would think to have to specify the component
+subtype in an iterator over its components, particularly if it is a long-winded
+name. It feels like an annoying "test" that doesn't (im)prove anything.
+
+****************************************************************
+
+From: Randy Brukardt
+Sent: Friday, February 12, 2010 3:44 PM
+
+...
+> My idea would be to have an aspect like Literal_Value that's called
+> implicitly, so:
+>
+> X : T := "Hello";
+> Y : T2 := 123;
+>
+> is equivalent to
+>
+> X : T := T'Literal_Value("Hello");
+> Y : T2 := T'Literal_Value("123");
+>
+> This AI is all about syntactic sugar, so I thought maybe I could sneak
+> this in. ;-)
+
+I'm in favor of this idea. But we looked at it last time, and concluded that it
+would make most Unbounded_String operations ambiguous to call with literals. So
+the #1 use for it wouldn't work.
+
+Consider Append, for instance. It is defined as:
+
+ procedure Append (Source : in out Unbounded_String;
+ New_Item : in Unbounded_String); -- (1)
+
+ procedure Append (Source : in out Unbounded_String;
+ New_Item : in String); --(2)
+
+ procedure Append (Source : in out Unbounded_String;
+ New_Item : in Character);
+
+So a call
+
+ Append (UBS, "Whatever");
+
+works today.
+
+If you add an automatic conversion possibility, this call becomes ambiguous, as
+both (1) and (2) would match. The only way out would be a preference rule, and
+those are typically trouble.
+
+P.S. I'd rather discuss the various unrelated subjects separately, so that
+people can comment on the one thing that interests them and not a litany of
+other things. Thus several separate answers.
+
+****************************************************************
+
+From: Bob Duff
+Sent: Friday, February 12, 2010 4:03 PM
+
+> I'm in favor of this idea. But we looked at it last time, and
+> concluded that it would make most Unbounded_String operations
+> ambiguous to call with literals. So the #1 use for it wouldn't work.
+
+Good point. And there is probably lots of existing code that has the same
+problem -- people add lots of overloadings precisely to get around the lack of
+user-defined literals.
+
+On the other hand, it would be useful for new packages.
+
+> If you add an automatic conversion possibility, this call becomes
+> ambiguous, as both (1) and (2) would match. The only way out would be
+> a preference rule, and those are typically trouble.
+
+Well, Tucker has argued recently that Beaujolais effects aren't so bad (or
+shouldn't be considered Beaujolais effects) if they're within a single package.
+Hmm...
+
+Or we could remove the offending overloadings. That would be incompatible, but
+not very much so...
+
+Or we could add Ada.Strings.New_Improved_Unbounded_Strings.
+
+Only half kidding here...
+
+****************************************************************
+
+From: Randy Brukardt
+Sent: Friday, February 12, 2010 3:54 PM
+
+> > Probably not absolutely necessary. Since we only have prefix
+> > notation for tagged types, it seemed natural to piggy-back on that.
+> > But these are really pretty different, I suppose, so we could
+> > consider defining these for untagged types.
+>
+> OK, good. Let's try to do that.
+
+I think it would be a good idea, but I don't see any way to do that without
+losing all of the existing magic that Tucker is leaning on.
+
+Currently, he has:
+
+ Container(...) equivalent to Container."()"(...)
+
+But of course the latter form isn't allowed for untagged types (for various good
+reasons).
+
+We could of course say the equivalence is instead to:
+
+ "()"(Container, ...)
+
+But that would prevent the use of the magic of prefix notation:
+(1) No use-clause needed for visibility;
+(2) Automatic lookup for class-wide operations;
+(3) Automatic addition of .all and 'Access as necessary.
+
+(2) of course doesn't matter for untagged types, but surely the other two do.
+Tucker's example in fact requires the automatic addition of 'Access. You'd never
+want to allow that on untagged types (unless the objects are aliased). We could
+duplicate all of that magic, but that seems like a mess. Or we could try to
+allow prefix notation on untagged types, but we already *know* that's a mess.
+
+> > > I see below that cursor types will have to be tagged to take
+> > > advantage of this notation. That's even more annoying, because
+> > > cursors can often be quite lightweight.
+> >
+> > I didn't mean for cursors to have to be tagged.
+> > Where does that requirement come in? It was not intentional.
+>
+> They have to be tagged if you want to use "()", which I think is
+> common, isn't it?
+
+You don't want to use "()" on a cursor, the cursor is the parameter. The
+parameter has no restrictions. And in any case, if you allow untagged "()"
+(which is appealing), then it doesn't matter.
+
+****************************************************************
+
+From: Randy Brukardt
+Sent: Friday, February 12, 2010 4:02 PM
+
+...
+> > > Do you mean "allows a constant" or "requires a constant"?
+> > > If the former, I think it introduces a Beaujolias effect.
+> > >
+> > > Would it make any sense to instead resolve on the basis of whether
+> > > the context requires a variable?
+> > > So for "X(C) := X(C) + 1;" the first would resolve to the variable
+> > > one, and the second to the constant one.
+> >
+> > That might be better.
+>
+> But it doesn't work if you rename X(C), right?
+
+I read this whole section as meaning that resolution already did this, not that
+you were proposing to change resolution.
+
+> >...I know this concerned Randy as well.
+> > I doubt if there is a Beaujolais effect, because this preference
+> >would presumably only apply to operators declared in the same scope.
+> >If the preference might affect resolution across packages, that
+> >would definitely be an issue.
+>
+> It's still a Beaujolais effect if it's within the same package. You
+> could argue that such Beaujolais effects are not so bad.
+
+But the "()" doesn't have to be in the same package, it just has to be in an
+ancestor (think class-wide lookup). And if you have two forms, they could be in
+different packages.
+
+Note that the alternative of *requiring* const/var matching would be pain if all
+you want is to define read access (which I think will be fairly common). In that
+case you'd still need to define two routines with the same result type.
+
+We could "fix" both problems by getting rid of the classwide lookup and
+requiring both routines to always be declared together (only as primitive
+operations of a type, and both are required). That seems like overkill, however.
+
+> I'm thinking that you have just the constant one.
+> X is a variable, and you say "Put(X(C));".
+> Then you add the variable version of "()", and now it silently
+> switches to that one (under one interpretation of your words).
+
+Right. The rule I suggest above would fix that, by not allowing there not to be
+a variable one. But it would clearly be a pain.
+
+> > I guess one important issue is whether this is a visibility issue at
+> > all. This is more like the prefix-notation than like a visibility
+> > lookup. So I see it more as "selecting" the right "()" operator
+> > rather than using normal overload resolution. Clearly the devil
+> > will be in the wording for this one, if we decide we want this
+> > functionality.
+>
+> Seems like we need this functionality, or the whole feature is rather
+> crippled.
+
+That's true, it's more like prefix-notation of an operator. One could simply
+duplicate the whole set of rules for prefix calls, and change the ones that we
+don't like or need. Sounds like a bigger mess, though.
+
+****************************************************************
+
+From: Tucker Taft
+Sent: Friday, February 12, 2010 4:08 PM
+
+Can you remind me of some of the "good reasons"
+why prefix notation is not permitted for untagged types? I can't remember the
+trouble they caused...
+
+****************************************************************
+
+From: Randy Brukardt
+Sent: Friday, February 12, 2010 4:31 PM
+
+> Can you remind me of some of the "good reasons"
+> why prefix notation is not permitted for untagged types? I can't
+> remember the trouble they caused...
+
+Gee, I don't remember either. I just remember we had problems and eventually
+gave up and just restricted to tagged. Better go look in the old AIs. Nice that
+we index them from the text changes: the numbers in question are AI95-0252 and
+AI95-0407 (based on 4.1.3(9.2/2) - there's a more recent fix to that, too, but
+it's not relevant).
+
+AI-0252 just has a paragraph at the end of the discussion:
+
+ We originally generalized this to support non-tagged types, but the
+ added complexity this brought to handling access types seemed more than
+ the anticipated benefit, since we would have to consider primitives of the
+ access type itself as well as those of its designated type.
+
+I think the problem comes about because of the automatic dereference. And I
+recall some pretty hairy examples of what that would cause. Probably have to go
+look up the minutes and/or read the old e-mail to recall the full extent. I'll
+leave that to you. (It strikes me that limiting "()" to composite types might
+effectively fix that problem.)
+
+****************************************************************
+
+From: Tucker Taft
+Sent: Friday, February 12, 2010 5:31 PM
+
+I also remember it being related to access types.
+If we stick to composite types for "()" that would seem to avoid the problem.
+Of course a private type might turn out to be an access type. Not sure what
+mischief that would cause...
+
+****************************************************************
+
+From: Randy Brukardt
+Sent: Monday, February 15, 2010 10:17 PM
+
+> I also remember it being related to access types.
+> If we stick to composite types for "()" that would seem to avoid the
+> problem. Of course a private type might turn out to be an access
+> type. Not sure what mischief that would cause...
+
+I thought of that, too. But I don't think that has to be a problem; "()" has to
+be primitive as you currently have it, and thus we could make it illegal to
+define it on an elementary type (even if we don't find out the type is
+elementary until the full definition -- it still has to be in the same package).
+I don't think that there is a problem if you *never* know that the type is
+elementary:
+
+ package P is
+ type Priv is private;
+ private
+ type Priv is access ...
+ end P;
+
+ with P;
+ package Q is
+ type My_Type is new P.Priv;
+ function "()" (O : aliased in out My_Type; ...)...
+ end Q;
+
+My_Type will never appear to be an access type to any user, even in the body of
+P, so you'll never have to consider ".all" of it.
+
+But this is sort of a last resort; if we can find a way to make these work
+without such a rule, that would be better still.
+
+****************************************************************
+
+From: Bob Duff
+Sent: Friday, February 12, 2010 12:57 PM
+
+> > Y : Unbounded_String := X & +", world.";
+>
+> You presumably don't need to use "+" for an operand of "&" since you
+> can overload that directly on String.
+
+True, but the whole point of having user-defined literals is to avoid having so
+many overloadings that just do "convert some args from String to T and then do
+the real work".
+
+> With a single Literal_Value aspect/attribute, it seems hard to know
+> which of the following should be legal for a given type:
+>
+> Y : T2 := "123";
+> or
+> Y : T2 := 123;
+
+Maybe both?
+
+> You might find my ideas about this in my pie-in-the-sky language
+> "ParaSail" interesting:
+
+I'm sure I would. I keep intending to pay attention to that, but I never seem
+to find the time.
+
+By the way, a blog seems like a rather unsuitable way to communicate a language
+design. How about giving me a Parasail Reference Manual? ;-)
+
+> Only the container needed to be tagged, not the "index" type (which is
+> where the Cursor fits in).
+> If I stated otherwise, that wasn't my intent.
+
+I guess I misunderstood.
+
+> > I'm thinking that you have just the constant one.
+> > X is a variable, and you say "Put(X(C));".
+> > Then you add the variable version of "()", and now it silently
+> > switches to that one (under one interpretation of your words).
+>
+> Oh, I see. That hardly seems like a maintenance issue. That almost
+> seems more like a feature.
+
+Perhaps it is.
+
+By the way, I guess you're normally going to have two "()" ops, and their bodies
+are going to be identical. That's a little bit annoying.
+
+> For an array it would be annoying I would think to have to specify the
+> component subtype in an iterator over its components, particularly if
+> it is a long-winded name.
+> It feels like an annoying "test" that doesn't (im)prove anything.
+
+Well, I suppose can buy that, but I'm not sure why you say "For an array". If
+it's annoying, isn't it equally annoying for any container-of-T, to have to
+specify T?
+
+Anyway, I'm happy to be _allowed_ to specify T.
+I think "for C: Character of S loop" is nicely readable.
+
+When I do "Iterate(X, Action => Blah'Access);" I am annoyed that Blah is in the
+wrong place, and that it's not anonymous, and that I have to say 'Access. But
+I'm not particularly annoyed that Blah's parameter's type is explicit.
+
+****************************************************************
+
+From: Randy Brukardt
+Sent: Friday, February 12, 2010 7:48 PM
+
+> True, but the whole point of having user-defined literals is to avoid
+> having so many overloadings that just do "convert some args from
+> String to T and then do the real work".
+
+OK, but you started this discussion with add this "so unbounded strings can be
+tolerable". But unbounded string *have* all of those overloadings, and we surely
+aren't going to remove them (the compatibility cost would be horrendous).
+
+Now, it would make sense to create a new package by taking Unbounded_Strings,
+giving the package a new name, replacing all occurrences of "String" by
+"Unbounded_String", and removing all of the duplicate routines. (We have to do
+this replacement because there are a number of routines that *only* take String
+parameters, and those have to be converted.) Then adding the new "()" and
+literals routines. But I tried such a suggestion last time and it got no
+traction (I was told "Unbounded_Strings" is good enough). And we tried a
+proposal much like this and abandoned it because it wouldn't help
+Unbounded_Strings.
+
+Thus I can see why this would be a good idea, but since we wouldn't be able to
+use it for anything (predefined), it's hard to imagine that it would be worth
+the effort.
+
+****************************************************************
+
+From: Tucker Taft
+Sent: Friday, February 12, 2010 10:49 PM
+
+> By the way, I guess you're normally going to have two "()" ops, and
+> their bodies are going to be identical. That's a little bit
+> annoying...
+
+I don't see them as identical in some cases.
+A read-only reference can be cheaper to manage than a read-write reference.
+There may be less or different locking, and if you are talking about a
+persistent data structure, there is no need to mark the page with the given
+element as "dirty" if the reference is read-only.
+
+****************************************************************
+
+From: Randy Brukardt
+Sent: Monday, February 15, 2010 10:18 PM
+
+> I also remember it being related to access types.
+> If we stick to composite types for "()" that would seem to avoid the
+> problem. Of course a private type might turn out to be an access
+> type. Not sure what mischief that would cause...
+
+I thought of that, too. But I don't think that has to be a problem; "()" has to
+be primitive as you currently have it, and thus we could make it illegal to
+define it on an elementary type (even if we don't find out the type is
+elementary until the full definition -- it still has to be in the same package).
+I don't think that there is a problem if you *never* know that the type is
+elementary:
+
+ package P is
+ type Priv is private;
+ private
+ type Priv is access ...
+ end P;
+
+ with P;
+ package Q is
+ type My_Type is new P.Priv;
+ function "()" (O : aliased in out My_Type; ...)...
+ end Q;
+
+My_Type will never appear to be an access type to any user, even in the body of
+P, so you'll never have to consider ".all" of it.
+
+But this is sort of a last resort; if we can find a way to make these work
+without such a rule, that would be better still.
+
+****************************************************************
+
+From: Brad Moore
+Sent: Wednesday, February 17, 2010 11:33 PM
+
+> > As usual, any and all comments welcome.
+
+I am very much in favor of the intent behind this proposal.
+
+I have have some suggestions however, that might further improve things. I admit
+I haven't fully thought these ideas through, but I wanted to see if they had any
+appeal.
+
+In a nutshell, if possible, I would like to see;
+ 1) Syntax even more harmonious with existing array/iterator syntax.
+ 2) An optional extension to the existing loop syntax to allow more
+ than one type of "magic" expansion. Essentially, allowing the
+ programmer to provide/override the implementation of the loop.
+
+For point 1, I am wondering if it would make sense to have syntax like;
+
+ for I in Container'Range loop
+ Container (I) := Container (I) + 1;
+ end loop;
+
+ instead of;
+
+ for Cursor in Iterate(Container) loop
+ Container(Cursor) := Container(Cursor) + 1;
+ end loop;
+
+
+ This would be more in line with existing syntax for iterating
+ through arrays. In this idea, the 'Range attribute would be user
+ definable for a container. Something like;
+
+package Ada.Containers.Doubly_Linked_Lists is
+
+ type List is tagged private;
+ type Cursor is private;
+
+ function First (Container : List) return Cursor;
+ function Last (Container : List) return Cursor;
+
+ package List_Range is new Ada.Range_Attribute
+ (Index_Type => Cursor,
+ Start => First,
+ Finish => Last);
+ for List'Range use List_Range.Magical_Thingy
+
+
+If such a construct is even possible, then perhaps one could also write
+
+ Position1 : Cursor := ...;
+ Position2 : Cursor := ...;
+
+ for I in Position1 .. Position2 loop
+ Container (I) := Container (I) + 1;
+ end loop;
+
+ Which seems useful.
+
+> a) The first syntax is intended for iterating over the set of cursors
+> or indices that select elements of a container. This is most
+> analogous to what is already available for arrays by writing "for I in
+> Arr'Range loop":
+>
+> for Cursor in Iterate(Container) loop
+> Container(Cursor) := Container(Cursor) + 1;
+> end loop;
+>
+> What the above presumes is that the function Iterate returns an object
+> of a type derived from a "magic" iterator type (see below). The
+> iterator type provides a First and Next operation which return a
+> Cursor type, and a No_Element cursor value to indicate end of
+> iteration. The above expands into:
+>
+> declare
+> _Iter : Iterator_Type := Iterate(Container);
+> Cursor : Cursor_Type := First(_Iter);
+> begin
+> while Cursor /= No_Element loop
+> Container(Cursor) := Container(Cursor) + 1;
+> -- This presumes an appropriate "()" operator
+> -- is defined.
+>
+> Cursor := Next(_iter, Cursor);
+> end loop;
+> end;
+>
+
+Could not this also expand to;
+
+ declare
+ procedure Iterate (Position : Lists.Cursor) is
+ begin
+ Container (Cursor) := Container (Cursor) + 1;
+ end Iterate;
+ begin
+ Container.Iterate (Iterate'Access);
+ end;
+
+Then there is less "magic" needed it seems to me.
+There is no need to involve an iterator interface with a function returning the
+First cursor, and other function returning the Next.
+
+This way, the expansion also works for containers where it doesn't make as much
+sense to start at the first element. For example, a recursive binary tree
+container would probably want to start the iteration at the root node of the
+tree, rather than start at the left most leaf node. If we can assume such an
+Iterate function has been defined, then the container implementation can decide
+how best to iterate through the container. As a further example, I have
+implemented a parallel recursive binary tree container which makes even less
+sense to think of starting the iteration at First, and iterating through the
+elements using Next. I do provide an Iterate procedure that manages the
+parallelism.
+
+> b) The second syntax is intended for iterating directly over the
+> elements of a container or an array:
+>
+> for Element of Container loop
+> Element := Element + 1;
+> end loop;
+>
+> or
+>
+> for Element of Arr loop
+> Element := Element + 1;
+> end loop;
+>
+
+These forms could also expand to the same expansion I suggested above, could
+they not?
+
+----------------
+
+Now the the second main suggestion.
+
+I would ideally like to see flexibility in the syntax so that the compiler could
+generate different expansions depending on the magical interface utilized by the
+programmer. For example, the expansions so far discussed in this AI center
+around making it easier to write iterators for containers, using a sequential
+approach. I would ideally like to be able to see expansions that would allow
+parallel executions through loops, including containers, arrays, and simple
+loops. The idea is that loop syntax would allow the programmer to optionally
+specify the name of a procedure/function used to implement the loop. Perhaps
+this procedure or function would be associated with a magical interface from a
+set of several available/defined interfaces so that the interface associated
+with the procedure would indicate to the compiler how the code should be
+expanded.
+
+For example,
+For the case of sequentially iterating through a container, one could optionally
+write something like;
+
+ for I in Container'Range with Container.Iterate loop
+ Container (I) := Container (I) + 1;
+ end loop;
+
+The with clause allows the programmer to specify the name of the procedure
+(implementation) to use for executing the iteration. This could default to name
+a primitive of the container such as "Iterate", which would be used if this was
+not specified by the programmer.
+
+i.e.
+ for I in Container'Range loop
+ ...
+
+ implies "with Container.Iterate" since that was not specified.
+
+
+Perhaps a different expansion would be like the one Tucker suggests for when you
+have a container where First and Next make sense, and where we would not want to
+expose an Iterate procedure, since the new syntax makes it easier to iterate
+through the container.
+
+Now consider a simple loop that we would like to execute in parallel.
+
+declare
+ Sum : Integer := 0;
+begin
+ for I in 1 .. 100_000_000 loop
+ Sum := Sum + I;
+ end loop
+end;
+
+We could use similar syntactic sugar to allow the programmer to write this as
+something like;
+
+with Integer_Addition_Reducer;
+-- an instantiation of a generic procedure
+
+declare
+ Sum : Integer := 0;
+begin
+ for I in 1 .. 100_000_000 with Integer_Addition_Reducer <Sum> loop
+ Sum := Sum + I;
+ end loop
+end;
+
+In this fantasy syntax, the variables enclosed by angle brackets (if
+any) represent variables that are modified by the loop but are declared in scope
+outside the loop. The generic needs to treat these specially to avoid race
+conditions when executing the loop in parallel.
+
+This might expand to;
+
+declare
+ Sum : Integer := 0;
+begin
+ declare
+ procedure Iteration (Start, Finish : Positive; Sum : in out Integer) is
+ begin
+ for I in Start .. Finish loop
+ Sum := Sum + I;
+ end loop;
+ end Iteration;
+ begin
+ Integer_Addition_Reducer
+ (From => 1,
+ To => 100_000_000,
+ Process => Iteration'Access,
+ Item => Sum);
+ end;
+
+I am currently working on a paper that utilizes such generics to assist in
+writing parallel loop iterations and parallel recursion. I am planning to
+present this at Ada Europe in Valencia, if anyone is interested. While I see
+these generics being useful without the benefit of syntactic sugar, some added
+sweetener would be nice, particularly if it is compatible with whatever syntax
+is used for regular container iterators.
+
+****************************************************************
+
+From: Randy Brukardt
+Sent: Thursday, February 18, 2010 12:47 AM
+
+...
+> > > As usual, any and all comments welcome.
+>
+> I am very much in favor of the intent behind this proposal.
+
+Good.
+
+> I have have some suggestions however, that might further improve
+> things. I admit I haven't fully thought these ideas through, but I
+> wanted to see if they had any appeal.
+...
+> For point 1, I am wondering if it would make sense to have syntax
+> like;
+>
+> for I in Container'Range loop
+> Container (I) := Container (I) + 1;
+> end loop;
+>
+> instead of;
+>
+> for Cursor in Iterate(Container) loop
+> Container(Cursor) := Container(Cursor) + 1;
+> end loop;
+
+Not really. The first form would only allow one iterator per container, and that
+would be a mistake. For instance, in the multiway tree containers, you might
+want to iterate on a particular set of children (starting from a particular
+node) or on the entire tree. That implies that we need multiple iterators and we
+need the capability of additional parameters. You also want the possibility of
+local storage for the iterator (such storage can't be in the container or you
+are restricting yourself to a single iteration at a time).
+
+Also recall that we have prefix notation, so the proposed form could easily be
+written:
+
+ for I in Container.Iterate loop
+ Container(I) := Container(I) + 1;
+ end loop;
+
+which is hardly different.
+
+...
+> If such a construct is even possible, then perhaps one could also
+> write
+>
+> Position1 : Cursor := ...;
+> Position2 : Cursor := ...;
+>
+> for I in Position1 .. Position2 loop
+> Container (I) := Container (I) + 1;
+> end loop;
+>
+> Which seems useful.
+
+You can write such an iterator easily. Indeed, I proposed one for the list
+container:
+
+ for I in Container.Partial_Iterate (Position1, Position2) loop
+ Container (I) := Container (I) + 1;
+ end loop;
+
+Here I'm using the possibility for parameters to the iterator creation function
+to control the iteration.
+
+...
+> Could not this also expand to;
+>
+> declare
+> procedure Iterate (Position : Lists.Cursor) is
+> begin
+> Container (Cursor) := Container (Cursor) + 1;
+> end Iterate;
+> begin
+> Container.Iterate (Iterate'Access);
+> end;
+
+No, as Bob and Tucker were talking about the other day, the loop has to support
+Exit (including multi-level exits) and Goto and all of the other transfers of
+control. A goto can't leave a subprogram body!
+
+...
+> This way, the expansion also works for containers where it doesn't
+> make as much sense to start at the first element. For example, a
+> recursive binary tree container would probably want to start the
+> iteration at the root node of the tree, rather than start at the left
+> most leaf node.
+
+I don't see this, as you can define First however you need for the iterator
+object. An iterator is *not* a container, and you don't have to use the same
+definition of First for the iterator as for the container. As Tucker notes,
+there is a single default iterator that allows you to use special syntax, but
+that is far too limiting for a general concept.
+
+> If we can
+> assume such an Iterate function has been defined, then the container
+> implementation can decide how best to iterate through the container.
+
+That's already true; the "Next" function can do whatever is appropriate for a
+particular iterator. It doesn't have to iterate in a particular order (although
+I would expect that most iterators would indeed have such an order).
+
+> As a further example, I have
+> implemented a parallel recursive binary tree container which makes
+> even less sense to think of starting the iteration at First, and
+> iterating through the elements using Next. I do provide an Iterate
+> procedure that manages the parallelism.
+
+A procedure seems appropriate for that, as it must not have an dependency
+between iterators nor any transfers of control. With a procedure body,
+dependencies are tough to write and exceptions are the only transfer of control.
+The body of a loop is a very different animal; it's easily nested in a declare
+block, and exits and returns are commonly used.
+
+> These forms could also expand to the same expansion I suggested above,
+> could they not?
+
+No, for the same reasons as noted above.
+
+> ----------------
+>
+> Now the the second main suggestion.
+>
+> I would ideally like to see flexibility in the syntax so that the
+> compiler could generate different expansions depending on the magical
+> interface utilized by the programmer. For example, the expansions so
+> far discussed in this AI center around making it easier to write
+> iterators for containers, using a sequential approach.
+> I would ideally like to be able to see expansions that would allow
+> parallel executions through loops, including containers, arrays, and
+> simple loops. The idea is that loop syntax would allow the programmer
+> to optionally specify the name of a procedure/function used to
+> implement the loop.
+> Perhaps this procedure or function would be associated with a magical
+> interface from a set of several available/defined interfaces so that
+> the interface associated with the procedure would indicate to the
+> compiler how the code should be expanded.
+
+I think that parallel loops would need to be syntactically marked, since the
+loop bodies would have to be written with a restricted set of features: no
+dependence between iterations, use of atomic variables/protected objects for
+sums and the like, and limited or no transfers of control. In an ideal world,
+those restrictions would be enforced by the compiler.
+
+> For example,
+> For the case of sequentially iterating through a container, one could
+> optionally write something like;
+>
+> for I in Container'Range with Container.Iterate loop
+> Container (I) := Container (I) + 1;
+> end loop;
+
+Huh? Now you're proposing something more complex to write and use than the
+current proposal, which is:
+
+ for I in Container.Iterate loop
+ Container (I) := Container (I) + 1;
+ end loop;
+
+And if you want to use another iterator for the same container:
+
+ for I in Container.Child_Iterate (Some_Cursor) loop
+ Container (I) := Container (I) + 1;
+ end loop;
+
+You're setting up a straw man and then knocking it down and proposing something
+more complex.
+
+A parallel loop construct isn't a bad idea, but it doesn't make much sense by
+itself. You'd want to define other constructs that also could be executed in
+parallel (parallel blocks and the like). Remember that Ada doesn't allow
+anything to be executed in parallel other than tasks; you'd surely want to be
+able to make function calls in parallel. And for all of those things, you'd want
+to define various restrictions on the statements in the loop/block/subprogram
+body. That requires dedicated syntax so that the compiler and/or reader can know
+about those restrictions.
+
+Please don't kill this nice sequential proposal by loading up all kinds of
+parallel cruft on it.
+
+****************************************************************
+
+From: Tucker Taft
+Sent: Thursday, February 18, 2010 8:09 AM
+
+I agree with Randy's comments on Brad's suggestions.
+
+****************************************************************
+
+From: Randy Brukardt
+Sent: Thursday, February 18, 2010 9:30 PM
+
+> > By the way, I guess you're normally going to have two "()" ops, and
+> > their bodies are going to be identical. That's a little bit
+> > annoying...
+>
+> I don't see them as identical in some cases.
+> A read-only reference can be cheaper to manage than a read-write
+> reference. There may be less or different locking, and if you are
+> talking about a persistent data structure, there is no need to mark
+> the page with the given element as "dirty" if the reference is
+> read-only.
+
+I've been thinking about this problem some more. I had almost convinced myself
+that this would work with some tweaks when I realized that there is an
+additional problem with this idea of having two function "()"s differing only by
+the constantness of the first parameter: we would have to allow homographs to be
+declared. For instance:
+ function "()" (Container : access constant Integer_Container; Index : in Natural) return Integer;
+and
+ function "()" (Container : access Integer_Container; Index : in Natural) return Integer;
+are homographs. In order for this resolution trick to work, we'd have to allow
+homographs (preferably only in this particular case!).
+
+That doesn't have much impact on the intended use, presuming that we give the
+prefix form Obj(Ind) special powers.
+
+However, it does have an impact on other uses of the functions. Any uses of the
+name "()" or Pack."()" are going to be ambiguous, because there will be two
+matching homographs (resolution uses type conformance to tell differences, which
+these surely are). For instance, normal calls like
+ Pack."()"(Obj, Ind);
+won't resolve.
+
+We could solve that by making the resolution rules depend on the name of the
+subprogram (which seems like a nasty wart - renames would work differently than
+the original subprograms), or by making a more general resolution change (which
+surely would have Beuajolias effects and possibly incompatibilities).
+
+We would also have similar problems with renames and uses as generic formal
+parameters. (The last isn't particularly important because a formal parameter
+couldn't be named "()" -- it is never primitive. But that too is weird.)
+
+I'm wondering if the proposal has gone off the rails a bit here by trying to
+have this new kind of subprogram with special resolution. It would be better if
+such magic was completely confined to the index-like call (which is new and
+isn't going to be a compatibility problem). As soon as we have an equivalent
+"normal" call, things start getting messy.
+
+So I've been thinking about alternatives. The obvious one would be to make these
+things a pair of aspects (I'm thinking "Variable_Indexing" and
+"Constant_Indexing"). Using aspects has several advantages:
+ * The routines are clearly tied to the type, so there isn't any big problem
+ supporting untagged types (or even scalar types);
+ * For normal calls, users use the "real" name of the functions, so there isn't
+ any resolution changes beyond the new magic notation;
+ * We don't need any funny rules about where the routines are declared -- the
+ need to specify the aspects would provide the appropriate result;
+ * It's not a problem to give the same function (name) to both aspects, so there
+ is no need to worry about having to duplicate the body;
+ * Legality of calls would depend only on whether an appropriate profile exists
+ for the appropriate aspect (and of course the normal rules for parameters);
+ * Changing the aspects of a type declaration surely has the possibility of
+ causing errors/changes down the line -- there is no doubt that it isn't
+ Beaujolais or anything like it.
+
+OTOH, these would be pretty weird aspects:
+ * The profile isn't fixed; it only needs the first parameter to be of the type;
+ * There shouldn't be any limit to the number of routines specified; the only
+ requirement beyond the first bullet is that the routines are not homographs.
+
+If we were to go this way, the "magic" *indexed call* would have at most two
+possible places to look for an operation: at the aspects of the prefix type, and
+if that is an access type, at the aspects of the designated type of the prefix
+type. The aspect to use would depend on whether or not the view of the prefix is
+a constant view (note that the object itself and the designated type could
+differ on that: dereferencing an access-to-constant variable would give a
+constant view.) Neither of these seem to have any particular difficulty. That
+would give one a set of profiles to resolve in the typical way: and no
+significant changes to resolution.
+
+One could imagine that arrays have a predefined version of these index aspects,
+with the obvious semantics; that would unify the syntax/semantics and avoid
+problems if someone tries to define these on an array type (otherwise we'd
+potentially have an ambiguity). Those would name a pair of anonymous functions
+like:
+
+ function <Constant_Indexing> (Arr : in Array_Type; Index : in
+Index_Subtype) return Component_Type;
+ function <Variable_Indexing> (Arr : access Array_Type; Index : in
+Index_Subtype) return access Ref_Component_Type;
+
+(The latter is a reference type.)
+
+If we go this way, I wonder if we would want a way to specify this aspect in
+generic contracts (it might make sense to be able to using indexing calls in a
+generic body).
+
+Anyway, food for thought.
+
+****************************************************************
+
+From: Tucker Taft
+Sent: Thursday, February 18, 2010 10:23 PM
+
+I agree the "()" idea seems to be creating several resolution issues. Using an
+aspect specification may be cleaner. If we are using an aspect specification
+for the Default_Iterator, it might be preferable to stick with that approach for
+other syntactic sugaring. The aspect specification might just be specifying the
+name of the Variable_Indexing and Constant_Indexing functions, which could then
+be overloaded as desired to provide multiple indexing functions.
+
+****************************************************************
+
+From: Brad Moore
+Sent: Friday, February 19, 2010 10:38 PM
+
+> > This way, the expansion also works for containers where it doesn't
+> > make as much sense to start at the first element. For example, a
+> > recursive binary tree container would probably want to start the
+> > iteration at the root node of the tree, rather than start at the
+> > left most leaf node.
+>
+> I don't see this, as you can define First however you need for the
+> iterator object. An iterator is *not* a container, and you don't have
+> to use the same definition of First for the iterator as for the
+> container. As Tucker notes, there is a single default iterator that
+> allows you to use special syntax, but that is far too limiting for a
+> general concept.
+
+I was thinking of a *recursive* binary tree container. I am having a difficult
+time seeing how one could use First and Next recursively to iterate through a
+tree. It is fairly easy to iterate through a tree non-recursively, and return a
+cursor to where the iteration last left off, and then picking up from where the
+iteration left off, with a call to Next. But I think this would be hard to do
+using a recursive stack-based approach. I suppose though, that for such a
+container, one would just have to use the Iterate procedure and not bother with
+this iterator syntax. If someone really wants to support the iterator syntax for
+such a container, they could provide a non-recursive First and Next as you
+suggest.
+
+> > As a further example, I have
+> > implemented a parallel recursive binary tree container which makes
+> > even less sense to think of starting the iteration at First, and
+> > iterating through the elements using Next. I do provide an Iterate
+> > procedure that manages the parallelism.
+>
+> A procedure seems appropriate for that, as it must not have an
+> dependency between iterators nor any transfers of control. With a
+> procedure body, dependencies are tough to write and exceptions are the
+> only transfer of control.
+
+Perhaps the proposed Preconditions, Postconditions would make it easier to
+specify the dependency rules.
+
+
+> I think that parallel loops would need to be syntactically marked,
+> since the loop bodies would have to be written with a restricted set
+> of features: no dependence between iterations, use of atomic
+> variables/protected objects for sums and the like, and limited or no
+> transfers of control. In an ideal world, those restrictions would be
+> enforced by the compiler.
+
+> A parallel loop construct isn't a bad idea, but it doesn't make much
+> sense by itself. You'd want to define other constructs that also could
+> be executed in parallel (parallel blocks and the like).
+
+I also have implemented generics that similarly make it easier to do parallel
+recursion. This allows me to write parallel functions and parallel procedures.
+Syntactic sugar might be able to provide a similar expansion to the parallel
+looping generics. The expansion is somewhat similar, but involves different
+generics.
+
+You have convinced me that it is probably not a good approach to try to unify
+the syntax between sequential iterators and parallel iterators. I am now
+thinking that instead, it would be better to try to unify syntax between
+parallel loops and parallel recursion.
+
+I think that a pragma based approach might work better.
+
+eg. Parallel looping:
+
+ Sum : Integer := 0;
+ for I in 1 .. 100_000_000
+ Sum := Sum + I;
+ end loop;
+ pragma Parallel_Loop (Work_Seeking_Integer_Addition_Reducer, Sum);
+
+In this case, Work_Seeking_Integer_Addition_Reducer is the name of a
+instantiated generic procedure, and Sum is the name of a global variable
+referenced within the loop.
+
+could expand to;
+
+ with Work_Seeking_Integer_Addition_Reducer;
+
+ Sum : Integer := 0;
+
+ declare
+ procedure Iteration
+ (Start : Integer;
+ Finish : in out Integer;
+ Others_Seeking_Work : not null access Parallel.Work_Seeking_State;
+ Sum : in
+ out Integer) is
+ begin
+ for I in Start .. Finish loop
+ Sum := Sum + I;
+ if Others_Seeking_Work.all then
+ Others_Seeking_Work.all := False;
+ Finish := I;
+ exit;
+ end if;
+ end loop;
+ end Iteration;
+ begin
+ Work_Seeking_Integer_Addition_Reducer
+ (From => 1,
+ To => Integer'Value (Command_Line.Argument (2)),
+ Process => Iteration'Access,
+ Item => Sum);
+ end;
+
+eg. Parallel Recursion:
+
+function Fibonacci (Number : Natural) return Natural is begin
+ if Number < 2 then
+ return Number;
+ else
+ return Fibonacci (Number - 2) + Fibonacci (Number - 1);
+ end if;
+end Fibonacci;
+pragma Parallel_Recursion
+ (Fibonacci, Two_Way_Recursive_Integer_Addition.Parallel_Recursion);
+
+In this case, Fibonacci is the name of the function to be executed in parallel,
+and Two_Way_Recursive_Integer_Addition.Parallel_Recursion is the name of a
+instantiated generic package.
+
+This could expand to the following;
+
+Note that this seems like a fair amount of code, it is mostly a template that
+follows a repeatable pattern. I used this same pattern to create recursive
+parallel iteration through a binary tree. The expansion essentially creates two
+versions of the code. One is the sequential version, which you want to execute
+most of the time, and the second version is the parallel version which gets
+executed when worker tasks become available to do more work. The parallel
+version looks very similar to the sequential version, but instead of calling the
+function recursively, it calls an instantiated function which ultimately causes
+a split in execution between available processors.
+
+with Two_Way_Recursive_Integer_Addition;
+use Two_Way_Recursive_Integer_Addition;
+
+function Fibonacci (Value :
+Natural) return Natural is
+
+ function Parallel_Fibonacci (Number : Natural) return Natural;
+
+ Supervisor : aliased Parallel_Recursion.Overall_Work_Supervisor
+ (Process => Parallel_Fibonacci'Access);
+
+ Others_Seeking_Work : aliased Parallel_Recursion.Work_Seeking_State;
+ use type Parallel_Recursion.Work_Seeking_State;
+
+ function Parallel_Fibonacci (Number : Natural) return Natural is
+ begin
+
+ if Number < 2 then
+ return Number;
+
+ elsif not Others_Seeking_Work then
+
+ return
+ Parallel_Fibonacci (Number - 2) +
+ Parallel_Fibonacci (Number - 1);
+
+ else
+ declare
+ Reduction : aliased Parallel_Recursion.Work_Team_Manager;
+ Sequential_Result : Natural;
+ begin
+
+ Sequential_Result :=
+ Parallel_Recursion.Recurse
+ (Number - 2,
+ Left,
+ Reduction'Unchecked_Access,
+ Supervisor) +
+ Parallel_Recursion.Recurse
+ (Number - 1,
+ Right,
+ Reduction'Unchecked_Access,
+ Supervisor);
+
+ -- Use the parallel result if work was split parallally,
+ -- otherwise use the sequential result
+ return Parallel_Recursion.Result (Reduction, Sequential_Result);
+ end;
+ end if;
+
+ end Parallel_Fibonacci;
+
+ Result : Integer := 0;
+
+begin
+ Parallel_Recursion.Initialize
+ (Supervisor'Unchecked_Access,
+ Others_Seeking_Work'Unchecked_Access);
+
+ Result := Parallel_Fibonacci (Value);
+ Parallel_Recursion.Finalize (Supervisor);
+ return Result;
+end Fibonacci;
+
+The pragmas would cause needed restrictions to be enforced within the parallel
+bodies. If a compiler vendor didn't want to support the parallel expansion, then
+they could ignore the pragma and the code would execute as it does today,
+sequentially.
+
+> Please don't kill this nice sequential proposal by loading up all
+> kinds of parallel cruft on it.
+
+It was definitely not my intent to kill this AI. I was looking to see if it
+could be expanded to cover both parallel and sequential iterators, since it
+seems on the surface that would be a fair bit of common ground between these two
+types of iterators. I see now that these are two vastly different beasts.
+
+I also wanted to minimize the chance that we might paint ourselves into a corner
+with new syntax that might preclude parallel support in the language in the
+future.
+
+I think you have convinced me that this is not a concern here.
+
+The question might be, is there anything that could be done will all this
+parallel cruft I have been rambling on about.
+
+Does it seem like a pragma based approach like I have suggested would be worth
+developing into an AI? Obviously, this is too late for the current amendment. We
+have a lot of AI's to deal with right now and so probably if these ideas have
+enough merit to develop into an AI, they should be more or less shelved until we
+get through the pile of other AI' waiting to be processed. I would certainly be
+willing to work on this in the background though, or at least I hope to create a
+white paper to better explain how these generics work.
****************************************************************
Questions? Ask the ACAA Technical Agent