CVS difference for ai12s/ai12-0197-3.txt

Differences between 1.1 and version 1.2
Log of other versions for file ai12s/ai12-0197-3.txt

--- ai12s/ai12-0197-3.txt	2016/10/06 03:36:32	1.1
+++ ai12s/ai12-0197-3.txt	2016/10/06 22:26:24	1.2
@@ -1,4 +1,4 @@
-!standard 6.9(0)                                      16-10-05  AI12-0197-3/01
+!standard 6.9(0)                                      16-10-06  AI12-0197-3/02
 !class Amendment 16-10-05
 !status work item 16-10-05
 !status received 16-10-05
@@ -84,7 +84,7 @@
 'Yielding attributes.
 
 'Next means
-   "run until reaching either a yield statement of the end of the generator
+   "run until reaching either a yield statement or the end of the generator
     body; if the former, then execute the yield statement and return the given
     result; if the latter then raise Program_Error". The type returned by the
     'Next attribute is the type after the yield in the generator declaration.
@@ -96,15 +96,19 @@
     indicates whether it is ok to call Next.
 
 A generator type is subject to the following constraints:
-
-1. Have to contain at least one yield statement. The grammar of the yield
-   statement would be the following:
 
-   yield_statement ::= "yield" expression ";"
+1. Have to contain at least one yield statement.
 
 2. Can contain return statements, as long as they have no expressions, in order
    to allow the user to terminate the execution early.
 
+We also allow yield statements to be contained by procedure directly or
+indirectly nested in the body of the generator.
+
+There are two yield statements, the regular one and the extended yield
+statement, as for return statements, and for the same reasons, namely
+build-in-place and limited types.
+
 Another way of consuming the generator is via the for .. of loop:
 
     P : Prime_Factory (1000);
@@ -128,17 +132,50 @@
 type, but not the implementation. This means that:
 
 1. Those types don't have a body.
+
 2. They're indefinite.
+
 3. Specific generator implementations, such as the one above, can be converted
-   to abstract generator types, provided the yielded subtype statically matches.
+   to abstract generator types, with rules closely following the rules to
+   convert specific tagged types to classwide types, with the added rule that
+   you can convert any specific generator type to any abstract generator type,
+   provided the yielded subtype statically matches. The accessibility rules
+   will be the same, as well as the implicit conversion rules. This means that:
+
+    --  This is illegal regarding accessibility rules.
+    type Positive_Generator yield Positive;
+    type Ref is access Positive_Generator;
+    Ptr : Ref;
+
+    procedure P is
+        generator type Local_Generator is ...;
+
+        function F yield Local_Generator is ...;
+    begin
+        Ptr := new Local_Generator'(F);
+
+
+    --  This is legal regarding conversion rules
+    type Positive_Generator yield Positive;
+    generator type Foo yield Positive;
+    ...
+
+    F : Foo;
+    G : Positive_Generator := F;
+
 4. 'Next and 'Yielding operations on instances/views of abstract generator types
    are dispatching.
 
+It is proposed that generators be non-synchronized entities: Nothing will be
+done to prevent unsynchronized access, and if the user wants to use one from
+several tasks concurrently, it shall be possible, but the user would have to
+wrap the generator in a protected object, or another synchronized construct.
+
 Syntax
 
 We propose introducing two new keywords, "yield" and "generator".
 
-We propose introducing these new object declarations:
+We propose introducing these new declarations:
 
 generator_type_declaration ::=
     "generator" "type" defining_identifier [known_discriminant_part]
@@ -155,6 +192,13 @@
 abstract_generator_type ::=
     "type" defining_identifier "yield" subtype_indication
 
+and two new statements:
+
+    yield_statement ::= "yield" expression ";"
+    extended_yield_statement ::= "yield" extended_return_object_declaration "do"
+        handled_sequence_of_statements
+        "end" "yield"
+
 ##implementation note
 
 While it might be possible to implement a limited version of this feature using
@@ -215,6 +259,33 @@
 
 !discussion
 
+# Nested yield statements issues
+
+It is strongly believed that we should allow yield statements in nested
+procedures in the generator. However, this might cause some problems, if an
+access to such a procedure is passed, and then called in an execution context
+that is not the one of the original generator. As usual with those kind of
+problems, we have two class of solutions:
+
+1. Dynamically disallow that, at run-time.
+2. Try to disallow that statically, by, for example, transitively forbidding
+access to procedures containing yield statements, or calling such procedures.
+
+Another issue raised by nested yield statements and extended yield statements
+is that you can yield inside of a yield, which should raise an exception:
+
+  procedure Foo is
+    begin
+      ...
+      yield (<something>);
+    end;
+  begin
+    Foo; -- ok
+    yield X : T do
+     ...;
+     Foo; -- This should raise P_E
+  end;
+
 # Generator singletons
 
 Since generators are very much like tasks, we probably want to allow to declare
@@ -1043,13 +1114,1176 @@
 
 ****************************************************************
 
+From: Jean-Pierre Rosen
+Sent: Sunday, September 11, 2016  2:07 AM
+
+Here is the generator, written using a task:
+
+with Text_IO; use Text_IO;
+procedure Test_Gen is
+
+   task type Prime_Factory (Sieve_Size : Natural) is
+      entry Next (Yield : out Positive)
+        with Post => Yield <= Sieve_Size;
+   end Prime_Factory;
+
+   task body Prime_Factory is
+       Is_Composite : array (2 .. Sieve_Size) of Boolean
+         := (others => False);
+       Current : Integer range Is_Composite'Range := 2;
+
+       procedure Mark_Multiples (Prime : Positive) is
+           Multiple : Positive := Prime;
+       begin
+           while Multiple <= Sieve_Size - Prime loop
+              Multiple := Multiple + Prime;
+              Is_Composite (Multiple) := True;
+           end loop;
+       end Mark_Multiples;
+    begin
+       while Current <= Sieve_Size loop
+          if not Is_Composite (Current) then
+             Mark_Multiples (Prime => Current);
+             accept Next ( Yield : out Positive) do
+                Yield := Current;
+             end Next;
+          end if;
+          Current := Current + 1;
+       end loop;
+    end Prime_Factory;
+
+    My_Gen : Prime_Factory (100);
+    Prime  : Positive;
+begin
+   while My_Gen'Callable loop
+      My_Gen.Next (Prime);
+      Put_Line (Positive'image(Prime));
+   end loop;
+end Test_Gen;
+
+1) The code is almost identical to the generator proposal
+
+2) The only significant difference is that the generator is a function; function
+   entries could be added to the language, which would be a welcomed addition,
+   independently of the generator proposal
+
+3) As written, the generator runs concurrently with the consumer. This could be
+   considered a benefit of this solution. IF sequential behaviour is required,
+   or IF there are fears about scheduling costs (but a generator will need some
+   kind of context switching anyway), THEN the notion of passive tasks can be
+   introduced. This notion can be a useful addition, independently of generators
+
+4) Generator interfaces come at no cost (we already have task interfaces)
+
+5) 'Next is simply an entry call. 'Yielding is simply 'Callable.
+
+Admitedly, there is a small race condition in the concurrent case, but I don't
+think it is a big deal at this point. In the passive task case, it is sufficient
+that evaluating 'Callable forces a "run until block or complete" on the target
+task. Note that 'Yielding on a generator will need to do the same anyway.
+
+--
+Honestly, I don't see the value of adding a whole load of syntax and complicated
+stuff for something that we can already do (and could already do in Ada 83!).
+Remember, Ada is about the "building blocks approach". We do have the main
+building blocks, some small blocks can be added (and could serve other purposes,
+which is the landmark of good building blocks). And don't forget that adding
+features not only complicates the compiler, it also makes teaching and promoting
+the language more difficult.
+
+Python people are very excited about generators, because they don't have high
+level tasking like Ada (only basic threads and synchronization, nothing close to
+the rendezvous AFAIK). I see no benefit in copying other languages workarounds
+for features that they miss and that we have.
+
+Hmmm... Where did I put that asbestos suit?
+
+****************************************************************
+
+From: Erhard Ploedereder
+Sent: Sunday, September 11, 2016  5:27 AM
+
+Lots of folks could immediately grasp the coroutine idea, which is why I like it
+(including the YIELD).
+
+Syntactic Suggestion: no need for a keyword.
+
+    [abstract] FUNCTION [type] identifier [discriminant_part] YIELD
+     subtype_mark;
+
+would do just fine. The YIELD vs RETURN tells the difference.
+
+****************************************************************
+
+From: Tucker Taft
+Sent: Sunday, September 11, 2016  8:36 AM
 
+It is a little weird to have both a "function type" and a "type is access
+function" but I agree that using the word "Yield" could be adequate to identify
+these as generators. The instances of such a "function type" are initialized
+generator objects that have a 'Next and a 'Has_Next attribute, whereas the
+"instances" of an access-function type are pointers to (as yet uncalled)
+functions.  If you omit the "type" then you sort of have to omit the parameters
+as well, because these become singletons that represent initialized generator
+objects, so there is no way to further parameterize them.
 
+There are some other weirdnesses -- the parameter passing semantics are somewhat
+different (more like discriminants than parameters), and the originally proposed
+syntax omitted the parameter list from the body and instead had "body"
+explicitly.  Presumably if we use "function" we would repeat the parameter list
+(including the "yield") and omit the "body" so it would look like a "normal"
+function body except for the "yield" instead of "return."
 
+I guess these begin to feel like pretty different beasts!
 
+****************************************************************
+
+From: Erhard Ploedereder
+Sent: Sunday, September 11, 2016  2:59 PM
+
+> Presumably if we use "function" we would repeat the parameter list
+> (including the "yield")
+
+yes.
+
+> and omit the "body" so it would look like a "normal" function body
+> except for the "yield" instead of "return."
+
+yes.
+
+The point being that this is a coroutine in all respects. "returning" (including
+running out of code) ends the generator/iterator, "yielding" continues it. Users
+would not have to worry about one or the other, no need for a racy "hasNext" or
+suchlike. Presumably, rather than "type" in the signature, one would write a
+generic coroutine instead. One less concept to worry about.
+
+****************************************************************
+
+From: Steve Baird
+Sent: Monday, September 12, 2016  6:56 PM
 
+> 2) The only significant difference is that the generator is a
+> function; function entries could be added to the language, which would
+> be a welcomed addition, independently of the generator proposal
+
+One important (IMO) part of the generator proposal is allowing yield statements
+in subprograms nested within the generator. One can, for example, traverse a
+recursive data structure using a recursive subprogram, yielding values along the
+way. This was mentioned in the proposal but not in the example. To provide this
+functionality using passive tasks, we'd have to be willing to relax the current
+restrictions on accept statement placement.
+
+There are also implementation issues associated with storage management and
+accessibility, but these may be pretty much the same with either approach. For
+example, if the generator-or-passive-task-or-whatever yields an indefinite
+subtype, then the caller may have to pass in a storage pool under the covers. I
+remember that the small-integer  implementation model for  runtime accessibility
+checking (which is  already broken for other  reasons) relied on not having to
+deal with entry functions.
+
+> 3) As written, the generator runs concurrently with the consumer. This
+> could be considered a benefit of this solution. IF sequential
+> behaviour is required, or IF there are fears about scheduling costs
+> (but a generator will need some kind of context switching anyway),
+> THEN the notion of passive tasks can be introduced. This notion can be
+> a useful addition, independently of generators.
+
+These guys are not intended to introduce another mechanism for concurrent
+execution. So I'd say, yes, sequential behaviour is required.
+
+Generators (at least as I envision them) don't even have the mutual exclusion
+guarantee of a protected record. They are like the iterators for the predefined
+container packages in that respect; if you want mutual exclusion, you have to
+add that yourself.
+
+If you or someone else would like to write up a "passive task" proposal, we
+could certainly consider it as an alternative to generators. My guess is that
+the other issues described in this message (indefinite result subtypes, accept
+statement placement restrictions) would be problematic, but that's just a guess.
+
+> Python people are very excited about generators, because they don't
+> have high level tasking like Ada (only basic threads and
+> synchronization, nothing close to the rendezvous AFAIK). I see no
+> benefit in copying other languages workarounds for features that they miss and
+> that we have.
+
+That's similar to the reaction that I had when I first heard about protected
+records. Why do we need these guys if we have tasks? I've since changed my mind
+on that one.
 
+****************************************************************
+
 From: Raphael Amiard
+Sent: Tuesday, September 13, 2016  5:33 AM
+
+>> 2) The only significant difference is that the generator is a function;
+>> function entries could be added to the language, which would be a
+>> welcomed addition, independently of the generator proposal
+>
+> One important (IMO) part of the generator proposal is allowing yield
+> statements in subprograms nested within the generator. One can, for
+> example, traverse a recursive data structure using a recursive
+> subprogram, yielding values along the way. This was mentioned in
+> the proposal but not in the example. To provide this functionality
+> using passive tasks, we'd have to be willing to relax the current
+> restrictions on accept statement placement.
+
+I agree that this is important.
+
+>> 3) As written, the generator runs concurrently with the consumer. This
+>> could be considered a benefit of this solution. IF sequential behaviour
+>> is required, or IF there are fears about scheduling costs (but a
+>> generator will need some kind of context switching anyway), THEN the
+>> notion of passive tasks can be introduced. This notion can be a useful
+>> addition, independently of generators.
+>
+> These guys are not intended to introduce another mechanism for
+> concurrent execution. So I'd say, yes, sequential behaviour is
+> required.
+>
+> Generators (at least as I envision them) don't even have the mutual
+> exclusion guarantee of a protected record. They are like the iterators
+> for the predefined container packages in that respect; if you want
+> mutual exclusion, you have to add that yourself.
+
+Having task safe behavior for generators would incur a performance
+penalty in single-task use cases, which will be the majority IMHO, so
+not a good thing.
+
+> If you or someone else would like to write up a "passive task"
+> proposal, we could certainly consider it as an alternative to
+> generators. My guess is that the other issues described in this
+> message (indefinite result subtypes, accept statement placement
+> restrictions) would be problematic, but that's just a guess.
+
+I would like the people who spoke up against passive tasks at the
+meeting (Tucker and Erhard come to mind) to contribute to this
+discussion. The only way I can sum up the argument against them right
+now is "If it looks like a Task, I'll expect it to behave like a task
+(regarding synchronization, and other properties), which it won't in
+this case.
+
+Personally, I have no strong preference for either option, certainly not
+enough to make a counter-proposal at this stage. I care about having the
+desired semantics, to express lazy iterators that can be used easily,
+for example, but that seems achievable following both proposals at the
+moment.
+
+One thing that *could* make me prefer the "separate construct" proposal,
+is if we decide that passive tasks needs to be synchronization safe,
+like protected objects. That will incur a performance penalty in
+single-threaded cases which I'd rather avoid.
+
+>> Python people are very excited about generators, because they don't have
+>> high level tasking like Ada (only basic threads and synchronization,
+>> nothing close to the rendezvous AFAIK). I see no benefit in copying
+>> other languages workarounds for features that they miss and that we have.
+>
+> That's similar to the reaction that I had when I first heard about
+> protected records. Why do we need these guys if we
+> have tasks? I've since changed my mind on that one.
+
+Python people are not really excited about generators, they consider
+them a given, it has been in the language since a very long time :)
+
+Also, I think you're making the mistake of considering the two
+constructs to have the same goals. Python users *do* miss something like
+Ada tasks, but that's not what they use generators for.
+
+That doesn't kill the idea of passive tasks of course, but it puts it in
+perspective: It would need to satisfy the use cases generators are
+actually used for (lazy sequences for example). The sieve example that
+you converted to tasking has the advantage of concurrent execution, but
+it's also a drawback:
+
+- You will have much more overhead spawning the task, which in some
+  cases will make the approach counter productive
+- You will make any correctness analysis of the produced code much
+  harder
+- You will make it unusable in contexts where tasks/threads are
+  disallowed.
+
+This would be solved by passive tasks, which is why I'm not against
+them.
+
+****************************************************************
+
+From: Jean-Pierre Rosen
+Sent: Wednesday, September 14, 2016  6:21 AM
+
+>> 2) The only significant difference is that the generator is a
+>> function; function entries could be added to the language, which
+>> would be a welcomed addition, independently of the generator proposal
+>
+> One important (IMO) part of the generator proposal is allowing yield
+> statements in subprograms nested within the generator. One can, for
+> example, traverse a recursive data structure using a recursive
+> subprogram, yielding values along the way. This was mentioned in the
+> proposal but not in the example. To provide this functionality using
+> passive tasks, we'd have to be willing to relax the current
+> restrictions on accept statement placement.
+
+The restriction on accept statements in tasks is, AFAIK, due to making sure that
+a task accepts only its own entries. If generators allow any inner declaration
+(as they should), then you can declare a generator inside a generator, and
+you'll have the same problem to make sure that a procedure yields only on behalf
+of the right generator.
+
+[...]
+>> 3) As written, the generator runs concurrently with the consumer.
+>> This could be considered a benefit of this solution. IF sequential
+>> behaviour is required, or IF there are fears about scheduling costs
+>> (but a generator will need some kind of context switching anyway),
+>> THEN the notion of passive tasks can be introduced. This notion can
+>> be a useful addition, independently of generators.
+>
+> These guys are not intended to introduce another mechanism for
+> concurrent execution. So I'd say, yes, sequential behaviour is
+> required.
+
+They are not /intended/, but allowing them to run concurrently is a nice plus.
+It allows the generator to anticipate on the next value.
+
+> Generators (at least as I envision them) don't even have the mutual
+> exclusion guarantee of a protected record. They are like the iterators
+> for the predefined container packages in that respect; if you want
+> mutual exclusion, you have to add that yourself.
+>
+> If you or someone else would like to write up a "passive task"
+> proposal, we could certainly consider it as an alternative to
+> generators. My guess is that the other issues described in this
+> message (indefinite result subtypes, accept statement placement
+> restrictions) would be problematic, but that's just a guess.
+
+Actually, it's not exactly a "passive task" for generators proposal. I argue
+that Ada already has the necessary building blocks to provide the fundamental
+functionality of generators: providing values when needed, without constructing
+the whole set.
+
+I view passive tasks (as well as function entries) as independent improvements,
+that could be useful for generators as well as other use cases.
+
+Moreover, the more I think about it, the more I think that generators will have
+to deal with exactly the same issues as tasks. Which is not surprising: after
+all, they ARE independent threads of control that maintain a state independently
+from the callers.
+
+>> Python people are very excited about generators, because they don't
+>> have high level tasking like Ada (only basic threads and
+>> synchronization, nothing close to the rendezvous AFAIK). I see no
+>> benefit in copying other languages workarounds for features that they miss
+>> and that we have.
+>
+> That's similar to the reaction that I had when I first heard about
+> protected records. Why do we need these guys if we have tasks? I've
+> since changed my mind on that one.
+
+I understand that position, but it is a matter of ROI between the cost of adding
+this to the language and the benefits. From the previous discussions, I fear the
+cost is high, it's somehow duplicating what has been done with tasks, with low
+benefit.
+
+****************************************************************
+
+From: Jean-Pierre Rosen
+Sent: Wednesday, September 14, 2016  6:31 AM
+
+> Having task safe behavior for generators would incur a performance
+> penalty in single-task use cases, which will be the majority IMHO, so
+> not a good thing.
+
+I don't think that it would require more than global lock, IF we want it to be
+task safe (for passive tasks, active ones are inherently task safe).
+
+>> If you or someone else would like to write up a "passive task"
+> proposal, we could certainly consider it as an alternative to
+> generators. My guess is that the other issues described in this
+> message (indefinite result subtypes, accept statement placement
+> restrictions) would be problematic, but that's just a guess.
+
+Maybe, but I would be surprised if the same problems don't arise with iterators.
+
+> Python people are not really excited about generators, they consider
+> them a given, it has been in the language since a very long time :)
+>
+> Also, I think you're making the mistake of considering the two
+> constructs to have the same goals. Python users *do* miss something
+> like Ada tasks, but that's not what they use generators for.
+
+My remark was the other way round: generators are easily constructed with tasks.
+If you need generators without tasks, you need a special construct.
+
+> That doesn't kill the idea of passive tasks of course, but it puts it
+> in
+> perspective: It would need to satisfy the use cases generators are
+> actually used for (lazy sequences for example). The sieve example that
+> you converted to tasking has the advantage of concurrent execution,
+> but it's also a drawback:
+>
+> - You will have much more overhead spawning the task, which in some
+>   cases will make the approach counter productive
+
+I learned to be careful when talking about efficiency without actual measures...
+A generator will also need its own context, its own stack...
+
+> - You will make any correctness analysis of the produced code much
+>   harder
+> - You will make it unusable in contexts where tasks/threads are
+>   disallowed.
+>
+> This would be solved by passive tasks, which is why I'm not against
+> them.
+
+Right
+
+****************************************************************
+
+From: Erhard Ploedereder
+Sent: Thursday, September 15, 2016  4:42 AM
+
+To me, the question whether "yield" is tied to the immediately enclosing
+construct is a primary issue, based on which the language concepts will differ
+considerably. (A "yield" anywhere in a nesting of subprogram calls is a
+troublesome notion much like an "accept" anywhere, which was disallowed for that
+reason.)
+
+In the absence of a nesting requirement, the coroutine principle is simpler than
+a tasking model because it is a strictly sequential construct vis-a-vis its
+caller and a lot less cumbersome than a "has-more"/"get-next" setup.
+
+As to parallel use, I am on the fence. But, if containers are not task safe, I
+see no reason to treat generators differently.
+
+****************************************************************
+
+From: Tucker Taft
+Sent: Thursday, September 15, 2016  12:18 PM
+
+I wonder whether an alternative approach might be to provide a simple type like
+Go's "channel," effectively a queue or pipe, which might or might not provide
+any buffering.
+
+There would be an operation to put things into the channel, and an operation to
+get things out of the channel, and an ability to put in an end-of-channel
+indicator, and an ability to detect the end-of-channel indicator.  The special
+thing about such a channel is that when a caller tries to put into a full
+channel, or get from an empty channel, it is suspended and the thread/coroutine
+suspended waiting on the "other end" is awakened.
+
+If there is nothing suspended on the other end, then the caller gets an
+indication that the channel is finished, which it could treat as an error or a
+normal termination indication.
+
+There could be syntactic sugar to make this all pleasant to use.  The main point
+is that you could have multiple channels associated with a single coroutine, so
+you could easily write various kinds of multiplexors (splitters, junctions,
+etc.) which combine channels in interesting ways.
+
+The idea is to separate the channel from the coroutine, rather than linking them
+tightly. At this point a coroutine becomes very nearly a passive task, and
+syntactic sugar could be used to create special cases like generators
+(one-to-one connection between a channel and a coroutine), but we would have a
+common underlying semantics.
+
+You could imagine having synchronized channels which could be used with "real"
+tasks, and unsynchronized, single-threaded channels, for use with
+coroutine/passive tasks.
+
+Even Ada's task entries could be described using this channel model, with each
+entry represented by a channel going into the task, and a return channel if
+there are any output parameters.
+
+One of the main things I don't like about the classic coroutine model is that
+coroutines need to name one another to pass control back and forth.  Generators
+are much friendlier in that the generator doesn't need to know the name of the
+thread requesting the data. The channel model moves further away from tasks
+having to know each other's name.  All they need is access to an open channel
+and they can generate or receive values.
+
+Some examples are clearly needed to evaluate this.  I'll try to create some over
+the next few days...
+
+***************************************************************
+
+From: Tucker Taft
+Sent: Thursday, September 15, 2016  12:29 PM
+
+One nice feature of this is that it finesses the issue of nested
+yields/accepts/etc. Since the channel is an object, you can pass it around as
+needed, including across recursive calls.  If it is an "unsynchronized" channel,
+then you don't want to pass it to a separate (active) task, but other than that,
+you could pass it anywhere you want, and use it how you see fit.
+
+Obviously an appropriate channel should be usable with the new Ada 2012 iterator
+syntax, so you can "drain" a channel using a "for X of Channel" loop.  But you
+could also explicitly Get items out of the channel.
+
+Now that we have the aggregate that has a "for" inside it, e.g. "(for I in 1 ..
+10 => I**2)" aka "iterated component association," perhaps we could also "fill"
+a channel using something like:
+
+    Channel := (for I in 1..10 => I**2);
+
+  or
+
+    My_Channel : Channel_Type is (for I in 1..10 => I**2);
+
+Isn't language design fun?
+
+****************************************************************
+
+From: Steve Baird
+Sent: Thursday, September 15, 2016  12:49 PM
+
+> To me, the question whether "yield" is tied to the immediately
+> enclosing construct is a primary issue, based on which the language
+> concepts will differ considerably. (A "yield" anywhere in a nesting of
+> subprogram calls is a troublesome notion much like an "accept"
+> anywhere, which was disallowed for that reason.)
+
+I think
+   a) the issue is important
+   b) yield statements in nested subprograms need to
+      work (e.g., inside a subprogram which is passed
+      to some container's iterator routine via an
+      access-to-subprogram parameter)
+   c) we need runtime checks to handle the case where the
+      yield statement is executed in the wrong context; we don't
+      want this to be erroneous
+   d) those runtime checks will be as costly (in
+      both complexity and performance) as the analogous
+      checks would be if we decided to allow accept statements
+      inside of a subprogram declared inside of the corresponding
+      task body. For example, an implementation could save the
+      current task_id in a variable declared at generator body
+      level when a generator starts executing (i.e., whenever
+      'Next or 'Has_Next is called) and check that it matches
+      the current task when a yield statement is executed.
+      [The generator could also set the variable to null when
+      'Next or 'Has_Text completes and check that the variable
+      is null when 'Next or 'Has_Next are called. This would detect
+      attempts to have two 'Next/'Has_Next calls in progress at
+      once, at least in the sequential case (the race condition
+      case could also be handled, depending on the details of how
+      this is implemented). So we might get additional benefits
+      out of this mechanism beside allowing nested-subprogram
+      yield statements.]
+      For a task body the task_id variable could be a constant,
+      initialized during task body elaboration, and checked
+      when an accept statement inside a subprogram is executed.
+
+Before deciding to support this, we'd need to decide whether we are really doing
+users a favor by introducing another construct which depends for correctness on
+being executed by a particular task. We already have this situation with, for
+example, most calls to Current_Task (in particular, most clients of
+Ada.Task_Attributes). I think the benefits of supporting nested-subprogram yield
+statements outweigh the costs, but this issue is (IMO) one of the costs.
+
+...
+> I wonder whether an alternative approach might be to provide a simple
+> type like Go's "channel,
+
+...
+> One nice feature of this is that it finesses the issue of nested
+> yields/accepts/etc
+
+Tuck - That does sound appealing. I look forward to seeing your example.
+
+****************************************************************
+
+From: Brad Moore
+Sent: Friday, September 16, 2016  10:05 AM
+
+> I understand that position, but it is a matter of ROI between the cost
+> of adding this to the language and the benefits. From the previous
+> discussions, I fear the cost is high, it's somehow duplicating what
+> has been done with tasks, with low benefit.
+
+I have similar concerns that the language already has ways to do this sort of
+thing with similar ease of use.
+
+I see this as a typical producer/consumer problem.
+
+Consider a buffer object as a "generator", where the buffer blocks on reading if
+the buffer is empty, and blocks on writing if the buffer is full. A buffer could
+be sized to hold a small number of objects, or even a single object.
+
+The buffer might even be a streamable object.
+
+I have several such buffer types in the dequesterity buffer project, for
+example.
+
+    http://dequesterity.sourceforge.net
+
+eg. See the Stream_Buffers.Ravenscar_Bounded package for a streamable buffer
+that supports the above blocking semantics;
+
+The client of the "generator" buffer calls 'Read whenever it needs another
+value.
+
+The producer is the generator implementation that is a procedure called from a
+dedicated task that calls 'write to push successive values into the generator as
+they are needed.
+
+The generator implementation could be generisized for ease of use so that the
+task is hidden, and the client code could just be a procedure that  is passed an
+access to the the generator buffer, and simply is a loop that writes values to
+the buffer.
+
+It strikes me that this achieves a similar effect as the generator idea being
+proposed, without adding more complexity to the language, and yet providing a
+similar ease of use.
+
+As a stream object, there is a fair amount of flexibility, for producing things
+like indefinite types, and for class wide object factories, etc.
+
+****************************************************************
+
+From: Raphael Amiard
+Sent: Friday, September 16, 2016  11:06 AM
+
+> It strikes me that this achieves a similar effect as the generator
+> idea being proposed, without adding more complexity to the language,
+> and yet providing a similar ease of use.
+
+I disagree about the latest part (about ease of use). If I understand you
+correctly, you only talk about the external semantics of the generator (the
+aspect were it provides a stream of objects). You can achieve a similar effect
+with an iterator object (just an object with a Next).
+
+Where generators shine is expressing the logic of your iterator, allowing you
+not to have any state to maintain between iterations - which, as Steve outlined,
+gets particularly interesting when that state is a stack of recursive calls.
+
+We have such an iterator in Libadalang by the way, look at
+https://github.com/AdaCore/langkit/blob/master/langkit/support/langkit_support-tree_traversal_iterator.adb#L7
+
+This is the kind of code that generators are meant to simplify. Indeed, this is
+just a few lines of code (and no type declaration and all) with generators.
+
+If you are talking about using tasks feeding a buffer, this could indeed work,
+and would provide similar expressivity, but is extremely heavy-weight in terms
+of run-time. I do not want to spawn a task every time I want to find something
+in a tree - or, for that matter, have a long running task for that purpose.
+
+****************************************************************
+
+From: Randy Brukardt
+Sent: Friday, September 16, 2016  11:37 AM
+
+I agree with Brad, and also have implementation concerns for existing compilers.
+
+****************************************************************
+
+From: Jean-Pierre Rosen
+Sent: Friday, September 16, 2016  2:50 PM
+
+> If you are talking about using tasks feeding a buffer, this could
+> indeed work, and would provide similar expressivity, but is extremely
+> heavy-weight in terms of run-time. I do not want to spawn a task every
+> time I want to find something in a tree - or, for that matter, have a
+> long running task for that purpose.
+
+I'd like to have some data about that "extremely heavy weight". Are you talking
+about memory space, efficiency? And are you sure that generators would be
+significantly lighter? A generator will need a context switch much similar to a
+task switch.
+
+****************************************************************
+
+From: Brad Moore
+Sent: Saturday, September 17, 2016  12:44 AM
+
+>> It strikes me that this achieves a similar effect as the generator
+>> idea being proposed, without adding more complexity to the language,
+>> and yet providing a similar ease of use.
+>
+> I disagree about the latest part (about ease of use). If I understand
+> you correctly, you only talk about the external semantics of the
+> generator (the aspect were it provides a stream of objects). You can
+> achieve a similar effect with an iterator object (just an object with
+> a Next).
+>
+> Where generators shine is expressing the logic of your iterator,
+> allowing you not to have any state to maintain between iterations -
+> which, as Steve outlined, gets particularly interesting when that
+> state is a stack of recursive calls.
+
+I see no difference, since the task has its own stack, there is no state that
+needs to be saved, and recursive calls work fine.
+
+To illustrate, I implemented a generator that iterates through a binary tree of
+nodes.
+
+Here is the client code with both the generator implementation, and the client
+usage.
+
+with Integer_Tree;
+with Generators;
+with Ada.Text_IO; use Ada.Text_IO;
+
+procedure Test_Generators is
+
+    Tree : constant Integer_Tree.Tree := Integer_Tree.Create;
+    use type Integer_Tree.Node_Access;
+
+    -- Implementation of the generator. Note this is a recursive
+    -- call.  Since the 'Write is blocking, the state of the stack
+    -- saved until the next value is read by the client.
+    procedure Tree_Iterate (Gen : Generators.Generator) is
+       procedure Next (Root : Integer_Tree.Tree) is
+       begin
+          if Root.Left /= null then
+             Next (Root.Left.all);
+          end if;
+
+          Integer'Write (Gen, Root.Value);
+
+          if Root.Right /= null then
+             Next (Root.Right.all);
+          end if;
+       end Next;
+    begin
+       Next (Tree);
+    end Tree_Iterate;
+
+    -- Instantiating the generator using the implementation above
+    package My_Generator is new
+      Generators.Implementation (Processing => Tree_Iterate);
+
+    Gen : constant Generators.Generator := My_Generator.Create;
+    Value : Integer;
+begin
+    -- The client Code that reads values out of the generator
+    while not My_Generator.Done loop
+       Value := Integer'Input (Gen);
+       Put_Line ("Value is" & Integer'Image (Value));
+    end loop;
+end Test_Generators;
+
+How much of a difference would there be to code the example above using the
+proposed generator syntax? I suspect there wouldn't be significant difference in
+terms of ease of expression.
+
+To see my reusable Generator generic, it is as follows.
+
+with Stream_Buffers; use Stream_Buffers;
+
+package Generators is
+
+    type Generator is access all Streamable_Buffer'Class;
+
+    generic
+       with procedure Processing (Gen : Generator);
+    package Implementation is
+       function Create return Generator;
+       function Done return  Boolean;
+    end Implementation;
+
+end Generators;
+
+with Stream_Buffers.Ravenscar_Bounded;
+
+package body Generators is
+
+    package body Implementation is
+
+       Still_Processing : Boolean := True;
+
+       Bounded_Ravenscar_Buffer : aliased
+         Stream_Buffers.Ravenscar_Bounded.Ravenscar_Buffer.Buffer
+           (Maximum_Capacity => 100);
+
+       Bounded_Stream_Store : aliased
+         Stream_Buffers.Ravenscar_Bounded.Buffer
+           (Data => Bounded_Ravenscar_Buffer'Access);
+
+       function Create return Generator
+       is
+       begin
+          return Bounded_Stream_Store'Unchecked_Access;
+       end Create;
+
+       task Generator_Implementation is
+       end Generator_Implementation;
+
+       task body Generator_Implementation is
+       begin
+          Processing (Bounded_Stream_Store'Unchecked_Access);
+
+          Still_Processing := False;
+       end Generator_Implementation;
+
+       function Done return Boolean is
+       begin
+          return not Still_Processing;
+       end Done;
+
+    end Implementation;
+end Generators;
+
+Also, the Tree package in case anyone wants to build the example, and try it
+out....
+
+package Integer_Tree is
+
+    type Node;
+    type Node_Access is access Node;
+
+    type Node is record
+       Value : Integer;
+       Left, Right : Node_Access := null;
+    end record;
+
+    subtype Tree is Node;
+
+    function Create return Tree;
+
+end Integer_Tree;
+
+with Ada.Numerics.Discrete_Random; use Ada.Numerics;
+
+package body Integer_Tree is
+
+    package Random_Integers is new Discrete_Random (Result_Subtype => Integer);
+
+    procedure Insert (New_Node : Node_Access;
+                      Root : in out Tree);
+
+    function Create return Tree is
+       Result : Node;
+       Gen : Random_Integers.Generator;
+    begin
+       Result.Value := Random_Integers.Random (Gen);
+
+       for I in 1 .. 100 loop
+
+          Insert (New_Node => new Node'(Value => Random_Integers.Random (Gen),
+                                        Left => null,
+                                        Right => null),
+                  Root => Result);
+
+       end loop;
+
+       return Result;
+    end Create;
+
+    procedure Insert (New_Node : Node_Access;
+                      Root : in out Tree) is
+    begin
+       if New_Node.Value < Root.Value then
+          if Root.Left = null then
+             Root.Left := New_Node;
+             return;
+          else
+             Insert (New_Node,
+                     Root.Left.all);
+          end if;
+       elsif New_Node.Value = Root.Value then
+          return;
+       elsif New_Node.Value > Root.Value then
+          if Root.Right = null then
+             Root.Right := New_Node;
+          else
+             Insert (New_Node,
+                     Root.Right.all);
+          end if;
+       end if;
+    end Insert;
+
+end Integer_Tree;
+
+For the Stream_Buffers packages, that can be found at
+dequesterity.sourceforge.net
+
+> We have such an iterator in Libadalang by the way, look at
+> https://github.com/AdaCore/langkit/blob/master/langkit/support/langkit
+> _support-tree_traversal_iterator.adb#L7
+>
+>
+> This is the kind of code that generators are meant to simplify.
+> Indeed, this is just a few lines of code (and no type declaration and
+> all) with generators.
+>
+> If you are talking about using tasks feeding a buffer, this could
+> indeed work, and would provide similar expressivity, but is extremely
+> heavy-weight in terms of run-time. I do not want to spawn a task every
+> time I want to find something in a tree - or, for that matter, have a
+> long running task for that purpose.
+
+It strikes me that this buffer implementation might actually be more efficient
+than the generator approach. With the generator syntax, my understanding is that
+a stack context switch needs to occur back and forth every time another value is
+read. With the buffer approach, the task typically has the next values to be
+read already sitting in the buffer, and to read the buffer, there is no context
+switch needed.
+
+The task in the generator object above automatically exits when the iteration if
+complete, but other schemes could be devised to terminate the task when it is no
+longer needed.
+
+****************************************************************
+
+From: Erhard Ploedereder
+Sent: Monday, September 26, 2016  6:04 PM
+
+> One nice feature of this is that it finesses the issue of nested
+> yields/accepts/etc. Since the channel is an object, you can pass it
+> around as needed, including across recursive calls.  If it is an
+> "unsynchronized" channel, then you don't want to pass it to a separate
+> (active) task, but other than that, you could pass it anywhere you
+> want, and use it how you see fit.
+
+It ought to be a limited object.
+
+In some channel models, passing the channel implies closing/terminating the
+channel for the passer. This also finesses the parallel access, since passing
+the channel across threads is a synchronizing event, so that at any time only
+one thread can feed off the channel, resp. put into the channel.
+
+(I like channels.)
+
+****************************************************************
+
+From: Florian Schanda
+Sent: Wednesday, September 28, 2016  5:20 AM
+
+> > I can feel the term "coroutine" coming on.
+>
+> But isn't "coroutine" a more general term than what we want? That is,
+> every generator is a coroutine but not every coroutine is a generator?
+
+It is, but its much clearer than anything relating "tasks".
+
+<rant>Ada already has this problem of having curious names for something
+everyone else has a different name (hello tagged types). Lets *not* make this
+worse.
+
+Task means there is some parallel processing going on and that is the emphasis.
+It also makes you think about locking, scheduling and waiting. This is *not*
+what a generator is about. It is educational on some level to think about these
+as tasks, but I don't think the purpose of the Ada language is to educate people
+who didn't do this stuff at University. :)</rant>
+
+How about "limited coroutine" or "semicoroutine". I know limited has a very
+different meaning in ada, but "limited coroutine" really describes quite well
+what a generator is: its a co-routine but with some limitations. It would also
+allow us later to add coroutines ;)
+
+****************************************************************
+
+From: Brad Moore
+Sent: Wednesday, September 28, 2016  9:10 AM
+
+>>> I can feel the term "coroutine" coming on.
+>>
+>> But isn't "coroutine" a more general term than what we want? That is,
+>> every generator is a coroutine but not every coroutine is a
+>> generator?
+>
+> It is, but its much clearer than anything relating "tasks".
+>
+> <rant>Ada already has this problem of having curious names for
+> something everyone else has a different name (hello tagged types).
+> Lets *not* make this worse.
+
+But for the generator library abstraction I presented, the use of a task is an
+implementation detail. Users of the abstraction do not need to be aware that a
+task is involved. One could in theory implement the abstraction using something
+lighter weight than tasks if there is some benefit to doing so.  I'm concerned
+about making the complexity of the language syntax worse when a similar
+capability can be provided using existing syntax. The argument that other
+languages had to create special gizmos because they don't have tasks, doesn't
+seem applicable to Ada.
+
+I think the concept of a generator is a useful abstraction, and could be worth
+standardizing in some form. I have not seen any convincing arguments about why
+this couldn't just be a library abstraction however.
+
+****************************************************************
+
+From: Tucker Taft
+Sent: Wednesday, September 28, 2016  9:20 AM
+
+>> One nice feature of this is that it finesses the issue of nested
+>> yields/accepts/etc. Since the channel is an object, you can pass it
+>> around as needed, including across recursive calls.  If it is an
+>> "unsynchronized" channel, then you don't want to pass it to a
+>> separate
+>> (active) task, but other than that, you could pass it anywhere you
+>> want, and use it how you see fit.
+>
+> It ought to be a limited object.
+
+For sure.
+
+> In some channel models, passing the channel implies
+> closing/terminating the channel for the passer. This also finesses the
+> parallel access, since passing the channel across threads is a
+> synchronizing event, so that at any time only one thread can feed off
+> the channel, resp. put into the channel.
+
+I have seen this called the "hand-off" model of parameter passing.  The language
+Hermes used that (as does ParaSail, and effectively SPARK as well).  Essentially
+if you give another entity a writable handle to something, you have no access to
+it until the other entity relinquishes that handle.
+
+> (I like channels.)
+
+Good to hear.
+
+****************************************************************
+
+From: Raphael Amiard
+Sent: Monday, October 3, 2016  1:53 PM
+
+> But for the generator library abstraction I presented, the use of a
+> task is an implementation detail.
+
+It is not an implementation detail I think:
+
+1. If you access any outer variable that is not part of the task body,
+you'll have potential race conditions. If you access synchronized
+objects you'll have potential deadlocks, etc.. You have all the problems
+of task based programming.
+
+2. Even for the use of the synchronization buffer, you'll have
+synchronization involved, which means overhead, but also a potential
+system call on some systems, which can be a very bad thing if you're
+calling that in a tight (soft-realtime) loop.
+
+3. Determinism. You control the order of execution of your generator.
+It doesn't matter in simple examples, but is a big big plus of
+generators in the real world.
+
+4. Those simpler characteristics also mean that the resulting feature
+will be *much* easier to use in some embedded footprints. Determinism +
+reduced runtime support means that it's much easier to certify code
+using generators than to certify code using tasks. It also means that
+having support in ZFP/SFP runtimes will also be much easier to
+implement.
+
+****************************************************************
+
+From: Raphael Amiard
+Sent: Monday, October 3, 2016  2:01 PM
+
+> How much of a difference would there be to code the example above
+> using the proposed generator syntax? I suspect there wouldn't be
+> significant difference in terms of ease of expression.
+
+No, I agree that your abstraction is quite expressive, and that there would
+likely be little difference. I detailed in my other answer why I still think
+generator semantics are useful. (cooperative, synchronous, deterministic, etc).
+
+> It strikes me that this buffer implementation might actually be more
+> efficient than the generator approach. With the generator syntax, my
+> understanding is that a stack context switch needs to occur back and
+> forth every time another value is read. With the buffer approach, the
+> task typically has the next values to be read already sitting in the
+> buffer, and to read the buffer, there is no context switch needed.
+
+Well, it depends on the implementation of the buffer, but if OS-level
+synchronization is involved, you'll probably have a system-call and a full
+context switch, which is much more costly than a stack switch. Anyway, we're
+clearly getting in the world of "let's profile this" :). Since we have a
+library-based generator implementation already, and your generator generic, I'll
+try to whip-up some benchmarks before the ARG meeting in Pittsburgh !
+
+****************************************************************
+
+From: Brad Moore
+Sent: Monday, October 3, 2016  2:23 PM
+
+That would be good to see, but I think streaming is slow in Ada, and not
+necessary for a generator. Also my buffer libraries were not designed for speed.
+It might be a better comparison to replace with the standard synchronized_queues
+library instead.
+
+****************************************************************
+
+From: Raphael Amiard
+Sent: Monday, October 3, 2016  2:27 PM
+
+> That would be good to see, but I think streaming is slow in Ada, and not
+>necessary for a generator. Also my buffer libraries were not designed for speed.
+>It might be a better comparison to replace with the standard
+>synchronized_queues library instead.
+
+No problem, I can use the synchronized queues instead of the streaming buffers,
+it seems like quite a minor change.
+
+****************************************************************
+
+From: Florian Schanda
+Sent: Tuesday, October 4, 2016  5:08 AM
+
+> 3. Determinism. You control the order of execution of your generator.
+> It doesn't matter in simple examples, but is a big big plus of
+> generators in the real world.
+
+I think all your points are really good (and I agree with all of them), but to
+me this is the single most important point.
+
+I think its plausible that an implementation *may* use a task behind the scenes,
+if it could somehow guarantee that nothing happens that would differ from these
+semantics, but to me that is an optimisation detail that we should not go into
+in the RM.
+
+****************************************************************
+
+From: Raphael Amiard
+Sent: Tuesday, October 4, 2016  1:37 PM
+
+Tucker, I'm amending my initial proposal, and I don't understand your suggestion
+below:
+
+>>What is missing from your proposal is, how do you go from the first form
+>>to the second. So, I have this Positive_Generator type declared
+>>somewhere, and I have this client as above. Can I use an instance of the
+>>Prime_Factory type and pass it to next with default ?
+>>
+>>1. If yes (which is what I would implicitly expect when reading your
+>>   proposal), then from what I can see, you have some of the same
+>>   problems that you'd have with the 'Generator approach: Is a generator
+>>   type yielding a Natural compatible with the Positive_Generator type ?
+>>
+>>2. If no, then you'd have to explicitly specify in the implementation
+>>   that it is deriving from Positive_Generator, which breaks the
+>>   modularity principle, and is annoying in practice:
+>
+>I believe Steve was requiring only that the yielded subtypes match statically,
+>which is a pretty well-defined requirement.  But then we might want to relax
+>that a bit to allow for classwide types, and perhaps unconstrained
+>discriminated types.  It turns out the matching rules required for "explicitly
+>aliased" parameters might be just about right.  See RM 6.4.1(6,6.1,6.2):
+>
+>"If the formal parameter is an explicitly aliased parameter, [...]. Further,
+>if the formal parameter subtype F is untagged:
+>6.1/3  the subtype F shall statically match the nominal subtype of the actual
+>       object; or
+>6.2/3  the subtype F shall be unconstrained, discriminated in its full view,
+>       and unconstrained in any partial view."
+
+>>... It means that you
+>>   cannot use a positive yielding generator implemented by somebody that
+>>   didn't know of your interface. Of course that can be worked around
+>>   with conversion rules (as it is done for other types). You also have
+>>   the "dummy type explosion" problem - Someday I'll count how many
+>>   "access all String" access types we have in GPS, but probably more
+>>   than 10 !
+>
+>I don't think we need to face these problems, if we use something close to
+>6.1/6.2 above for untagged subtype matching.
+
+How do you propose that we use those rules ? can you be more explicit about
+what you had in mind ?
+
+Thanks in advance !
+
+***************************************************************
+
+From: Raphael Amiard
 Sent: Wednesday, October 5, 2016  1:39 PM
 
 I did an updated version of the original AI, integrating Steve's suggestions.
@@ -1148,6 +2382,140 @@
 
     c) are users are responsible for preventing unsynchronized access
        (as with predefined container types)?
+
+****************************************************************
+
+From: Raphael Amiard
+Sent: Thursday, October 6, 2016  9:17 AM
+
+>> 'Next means
+>>     "run until reaching either a yield statement of the end of the
+>> generator
+>
+> typo: first "of" => "or"
+
+Will fix, thanks !
+
+> There might also be accessibility conditions that have to be satisfied
+> in some cases when converting to from a shorter-lived type to a
+> longer-lived type, but presumably these would be handled the same way
+> that the analogous issue is handled with conversions between specific
+> and class-wide types.
+>
+> One way or another we don't want to allow
+>
+>     type Positive_Generator yield Positive;
+>     type Ref is access Positive_Generator;
+>     Ptr : Ref;
+>
+>     procedure P is
+>         generator type Local_Generator is ...;
+>
+>         function F yield Local_Generator is ...;
+>     begin
+>         Ptr := new Local_Generator'(F);
+>
+> to execute successfully.
+
+Can I just write in the AI (for the time being):
+
+3. Specific generator implementations, such as the one above, can be converted
+   to abstract generator types, provided the yielded subtype statically
+   matches, and that the specific generator type satisfies the same rules
+   towards the abstract generator type as in the case of a conversion between a
+   specific and a class-wide type.
+
+> The proposal doesn't talk about the detail of whether an explicit
+> conversion is required vs. allowing the concrete-to-abstract
+> conversion to be done implicitly, as for a specific-to-classwide
+> conversion; if an explicit conversion is required that would solve
+> this problem but it would also make these guys harder to use because
+> "type conversion" isn't one of the constructs on the 7.5(2.1/5) list (the
+> "aggregates, function calls, and their friends" list).
+>
+> It seems like following the existing model for conversions between
+> class-wide and specific tagged types would be cleanest.
+
+Yes it doesn't talk about it because I didn't understand your and Tuck's views
+on the matter :) This makes it a bit clearer for me.
+
+> ====
+>
+>> We propose introducing these new object declarations:
+>>
+>> generator_type_declaration ::=
+>
+> Perhaps delete the word "object"?
+>
+
+You're right, will fix.
+
+> ====
+>
+>> Note that this code is already working code, using a runtime library
+>> that was developed on top of a portable stack switching library called PCL.
+>
+> Would this work in the build-in-place case (e.g., if the generated
+> type is Some_Limited_Type'Class)?
+>
+
+Probably not :) I'll have to check. When designing the prototype (and the first
+version of this AI) we didn't even think about limited types much.
+
+So I guess the real underlying question is "can it be made to work with B-I-P
+and limited types". I have no idea, so that can be some homework for the next
+ARG meeting.
+
+> ====
+>
+> Some questions we have discussed, but were not mentioned in the
+> proposal (which is fine, except that I'm curious):
+>
+>    a) are yield statements allowed in nested subprograms (unlike accept
+>       statements), with appropriate runtime checks to detect
+>       "inappropriate" execution of a yield statement?
+
+Yes, I actually sent the wrong version of the file to this thread, because I
+added it in the last version.
+
+> b) does an attempt to execute a yield statement during
+>       the execution of another yield statement raise P_E?
+>       (as we have discussed, I think it should).
+>
+
+Yes, another thing I forgot ! Adding it
+
+
+> c) are users are responsible for preventing unsynchronized access
+>       (as with predefined container types)?
+>
+
+Yes. I'll specify that as well.
+
+Thanks for the review Steve !
+
+****************************************************************
+
+From: Raphael Amiard
+Sent: Thursday, October 6, 2016  10:44 AM
+
+>> I did an updated version of the original AI, integrating Steve's
+>> suggestions
+> This is AI12-0197-3/01 (that is, the third alternative of the
+> generator proposal).
+
+Yes, absolutely. I find Steve's proposal much better than my original one: It is
+very close to the desired semantics of my proposal, while being much more
+Ada-idiomatic as far as definition and use of generators go.
+
+> I find it interesting that you essentially finished Steve's homework
+> for him
+
+He also in some way started my homework for me, and you have one less proposal
+to read, so everybody wins ;)
+
+Here is my amended version of it integrating Steve's remarks - I think it's
+still time right ? [This is version /02 of the AI - Editor.]
 
 ****************************************************************
 

Questions? Ask the ACAA Technical Agent