CVS difference for ai12s/ai12-0189-1.txt
--- ai12s/ai12-0189-1.txt 2016/06/07 05:08:52 1.2
+++ ai12s/ai12-0189-1.txt 2016/10/05 22:50:02 1.3
@@ -1,4 +1,4 @@
-!standard 5.5.2(2/3) 16-06-06 AI12-0189-1/02
+!standard 5.5.2(2/3) 16-10-04 AI12-0189-1/03
!class Amendment 16-06-02
!status work item 16-06-02
!status received 16-05-06
@@ -7,38 +7,37 @@
!subject loop-body as anonymous procedure
+Provide an iterator syntax which results in creating an
+access-to-procedure value designating a loop-body procedure, and passing
+that access value to a named procedure for iteration. This enables a
+convenient user-defined iteration mechanism that does not require
+defining a separate cursor-based iterator abstraction.
-[Editor's note: Both Randy and Tucker wrote problem statements for this AI
-and since they're rather disjoint, I'm presenting both. The next author will
-have to reconcile them:]
-Currently, the way to create an iterator for some abstraction is to create an
-implementation of the iterator interface. This requires the invention of a
-cursor type, and often the use of the Rosen technique (as the iterator object
-parameters of the iterator interface are of mode "in").
-But not all abstractions have natural cursors. Moreover, some have multiple
+There are several language-defined operations that provide iteration by
+taking an access-to-procedure from the caller and calling back to the
+designated procedure once for each element of the iteration. It would
+be nice if there were a convenient syntax for specifying the body for
+this call-back, without having to start a new declare-block and declare
+a named procedure only to pass the 'Access of it exactly once to the
+Currently the only way to create a user-defined iterator for some
+abstraction that can be used with a "for ... loop" is to create an
+implementation of the iterator interface. This requires the invention of
+a cursor type, and often the use of the Rosen technique (as the iterator
+object parameters of the iterator interface are of mode "in"). But not
+all abstractions have natural cursors. Moreover, some have multiple
items (key - value pairs are particularly common).
-Most of these abstractions (like Ada.Directories and
-Ada.Environment_Variables) already have closed iterators that do not expose
-cursors. If we could map to them, we could simplify many iterators without
-adding ridiculous amounts of new capabilities.
+Providing support for the access-to-procedure approach with "for ...
+loop" could provide a useful alternative for abstractions where the
+cursor approach doesn't work as well. Some of these abstractions (like
+Ada.Directories and Ada.Environment_Variables) already have closed
+iterators that do not expose cursors. If we could support them, we could
+simplify many iterators without adding multiple complex constructs.
-There are several language-defined operations that provide iteration by taking
-an access-to-procedure from the caller and calling back to the designated
-procedure once for each element of the iteration. It would be nice if there
-were a convenient syntax for specifying the body for this call-back, without
-having to start a new declare-block and declare a named procedure only to pass
-the 'Access of it exactly once to the iteration operation.
A loop body can be used to specify the implementation of a procedure to
@@ -73,56 +72,27 @@
equivalent to the actual for the last parameter of the called procedure
being specified as <>.
-An exit, return, goto, or other escape from such a loop will be implemented
-by an unhandlable exception, essentially the way asynchronous transfer of
-control is implemented. This will be buried in the "de-sugaring" process, and
-can be done in an implementation-defined manner.
+An exit, return, goto, or other transfer of control out of the loop is
+allowed. Such a transfer of control causes the named procedure to which
+the loop-body procedure is passed to be completed and left
+[Redundant(resulting in normal finalization)], followed by a transfer of
+control to the target of the original transfer of control that was
+within the loop. The procedure calling the loop-body procedure must use
+finalization if it wants to perform any "last wishes" when the loop body
+procedure exits prematurely via a transfer of control.
-If we want to define a portable mechanism for implementing this loop
-escape, either we specify use of asynchronous transfer of control, or we
-define a new kind of exception that is *not* handled by "others" but is
-instead only handleable when named explicitly. This might be called a
-"private exception." E.g., given a loop with an exit such as:
- for (Key, Elem) of My_Map.Iterate loop
- Put_Line(Key_Type'Image(Key) & " => " &
- exit when Key = 42;
- end loop;
+The implementation may use various techniques to implement this transfer
+of control out of the loop-body procedure. Possibilities include the
+mechanism used to implement asynchronous transfer of control (ATC), an
+unhandleable exception, or some more direct use of the implementation's
+finalization mechanism. One important simplification is that this
+transfer of control is not signaled by a separate task, but is rather
+caused by an action in the task executing the body designated by the
+access-to-procedure value. That could presumably make it simpler and
+safer than ATC.
-after being "de-sugared" the code for the loop might be:
- Exit_Loop : private exception;
- procedure Loop_Body(Key : Key_Type; Elem : Elem_Type) is
- Put_Line(Key_Type'Image(Key) & " => " &
- if Key = 42 then
- raise Exit_Loop;
- end if;
- end Loop_Body;
- when Exit_Loop =>
-An exception declared as "private" would not be caught by a "when
-others" inside the Iterate procedure, and since there is no way to name
-this locally declared private exception elsewhere, we know the only
-handler is the one provided by the de-sugaring. Multiple private
-exceptions could be declared if there are multiple ways the loop body is
-escaped (e.g. an exit, a goto, and a return).
-But as mentioned above, we can allow the implementation to support escaping
-a loop anyway it sees fit, so long as it can't be hindered by the
@@ -187,6 +157,17 @@
each element of the map, since we are using the normal semantics of
in-out parameters to provide access to the appropriate element.
+We considered various implementation approaches for handling a transfer
+of control from the loop body, including the notion of a "private"
+exception which was not handled by "others." We ultimately left it
+unspecified. Conceivably some future AI could try to define a portable
+mechanism, but this seems an unnecessary complication for this AI, and
+is almost certain to be less efficient than some implementation-specific
+approach, which can tap directly into the finalization mechanism used by
+the implementation. We made it clear that the only way to specify "last
+wishes" for a routine calling a loop-body procedure was to use
@@ -1197,5 +1178,74 @@
AI12-0009-1 and AI12-0188-1 are both unnecessary if this capability is
provided. (Which makes a nice lead-in for your examples, which illustrate that
+From: Tucker Taft
+Sent: Tuesday, October 4, 2016 10:26 PM
+Here is an update. I didn't make much of a change, except to decide that the
+transfer of control out of a loop body is permitted, the implementation has to
+support it, and the caller of a loop-body procedure has to use finalization if
+they have any "last wishes" they want honored if the loop-body procedures exits
+via a transfer of control.
+[This is version /03 of the AI - Editor.]
+From: Randy Brukardt
+Sent: Wednesday, October 5, 2016 5:02 PM
+> Here is an update. I didn't make much of a change, except to decide
+> that the transfer of control out of a loop body is permitted, the
+> implementation has to support it, and the caller of a loop-body
+> procedure has to use finalization if they have any "last wishes"
+> they want honored if the loop-body procedures exits via a transfer of
+Having had months to think about it, I don't think the latter works.
+I would guess that approximately 0% of existing "access call-back" style
+iterators use finalization for last wishes. That's much more complex than
+using an others exception handler, so it tends to get used only in cases where
+a simple exception handler isn't enough (access locks, for instance, where
+leaving a lock behind is fatal to the application). The exception handler is
+likely to be faster, too, especially when it is not used. But probably well
+over half of such iterators need last wishes (led by Ada.Containers, which need
+to clear the tampering flag).
+It's bad enough that such a requirement would force implementers to go in and
+rewrite their existing implementations of language-defined routines.
+That's annoying, but we could live with it.
+The problem is that the same would be true for any user-defined (or worst of
+all, third-party-defined) iterators. There is nothing in the specification of
+an iterator that would tell you whether or not it is safe [handles it's last
+wishes via finalization]. And it's unlikely that any existing iterators would
+document how (or if) they handle last wishes. So I think such an unenforced
+requirement would be very error-prone (it would be an attractive hazard, in
+that it would look like a convenient way to write such a loop, but it wouldn't
+actually work right in marginal cases; such cases could even escape testing,
+leaving a time-bomb in a program).
+I think that any such scheme has to work for any existing "access call-back"
+iterator (that is, any subprogram that would have the correct profile), since
+they're already reasonably common (especially as the pattern is given and
+encouraged in the Ada RM, starting with Ada 2005). Expecting the implementation
+of such an iterator to use an unusual method of supporting "last wishes" is not
+Ergo, I believe that non-exception transfer of control out of the loop has to
+be banned by such a proposal. (That is, exit and return statements aren't
+allowed, and gotos are only allowed within the loop body - we have to support
+the latter so the "continue" statement we don't have works.) That closely
+matches what is allowed for the original version of these routines, and I can't
+find any reason to think that this literal "syntax sugar" should try to do any
+more. And (at least so far), all of the alternatives lead to madness. (As
+always, banning something now leaves the door open a crack to allow it in the
+future if it proves to be a major issue; allowing it now means we're stuck with
+it forever, even if it turns out to be a significant problem. With this sort of
+ease-of-use feature, I would prefer to be conservative.)
Questions? Ask the ACAA Technical Agent