I only know Python's generator as such that it is created by a generator function (a function that has yield expr in it), and itself is a generator object.  Since it is an object I see it can have more than one code paths, but I'm unfamiliar with its usage than the trivial tasks (generating one value at at time via next method).  Could you point some references to see advanced generator usages?  Or, could you show an idea that how the API would look if we integrate generators and accumulators in Scheme?



On Tue, Oct 17, 2017 at 12:28 AM, Marc Nieper-Wißkirchen <xxxxxx@nieper-wisskirchen.de> wrote:
Shiro Kawai <xxxxxx@gmail.com> schrieb am Sa., 14. Okt. 2017 um 20:30 Uhr:
Allowing a generator g to accept arguments to "add" items into that generator is still a generator.  The point of accumulator is that you can get the result at once, and you don't need to know the type of the result to use it.

Python's generators allow to throw an exception into it. This makes Python's generator protocol flexible enough to model accumulators. Let me bring it face to face:

Python                                           | SRFI 158
=====================================================================================================================
Generator                                        | Generator or accumulator or something strictly more powerful
Generators yield values                          | Generators yield values
Values can be sent into generators               | Values can be sent into accumulators
Exhausted generator can have a non-trivial value | Exhausted generator has only a trivial value (eof-object)
Generators can be stopped by non-trivial values  | Accumulators can be stopped only with a trivial value (eof-object)

In other words, Python's generators accept values through two code paths (sending or throwing into) and return values through two code paths (yielding and throwing out of). In contrast, SRFI 158's generators accept values only through one code path (the sending equivalent) and return values only through one (the throwing equivalent, but restricted to a unique value). SRFI 158's accumulators accept values through two code paths (the sending and the throwing equivalent, but the latter restricted to a unique value), and return values only through one (the throwing equivalent).

One way to make SRFI 158's generators more powerful could be to add an optional payload to eof objects, which does not seem to contradict their specification in the R7RS. Then, accumulators can be come a derived concept of the generator concept.

It remains to discuss how Scheme's concept of multiple values fit into the proposed protocol. Generators yielding multiple values make perfect sense (think of a generator that pairs two other generators in parallel) but the current protocol makes this a bit hard to implement efficiently: Because the eof object will only be returned as a single value, the generator would not produce a constant number of values in each step.


The use case of accumulator I see is a generic gatherer---by passing accumulator, one can parameterize sequence operations.  For example, from an infinite generator of integers (size-gen) and an

I am not saying that accumulators (as those in the current draft of SRFI 158) are not useful, quite to the contrary. It's just that they shouldn't be called "duals" of generators when they are both subsumed by the more powerful generator concept of Python or ECMAScript (and which should make us think whether we can spec something at least as powerful now that SRFI 121 is being revised). Following the slogan: Few unified concepts, but powerful ones.

Marc

infinite generator of random items, you can write an infinite generator of random sequences, whose return type can be customized by passing suitable accumulators.

(define (random-sequence make-acc size-gen item-gen)
  (lambda ()
    (let ((acc (make-acc)))
      (let loop ((n (size-gen)))
         (if (zero? n)
            (acc (eof-object))
            (begin (acc (item-gen)) (loop (- n 1))))))))

Suppose (integers-poisson$ L) returns an infinite generator that generates random integers accoring to poisson distribtuion of mean L, and (chars$ CHAR-SET) returns an infinite generator that returns random chars from the given char-set.

(random-sequence string-accumulator (integer-poisson$ 5) (chars$ char-set:ascii))

returns a generator that generates random string.

Gauche's dara.random binary uses similar approach, although it uses classes (the callee can derive accumulator from a class object).  For example: http://practical-scheme.net/gauche/man/?p=sequences-of









On Sat, Oct 14, 2017 at 2:46 AM, Marc Nieper-Wißkirchen <xxxxxx@nieper-wisskirchen.de> wrote:
I am not so much concerned about the usability of the accumulator approach in this SRFI, but more about saying that the SRFI 158 concept of an accumulator is dual to the concept of a generator.

In fact, after some thinking, I have come to the belief that the SRFI 158 generators and accumulators are instances of the same, slightly more general concept.

Consider Python's (or ES6's) generators. Such a generator can both be fed a value, and values can be retrieved from such a generator. Also, such a generator has a return value. In other words, it can act as a SRFI 158 generator and as a SRFI 158 accumulator at the same time. Besides showing the applicability of Python's generator concept, it shows that SRFI 158's generator and accumulator concepts are not dual, but are shadows of the same unified concept.

Thus I would like to propose to extend SRFI 158's concept of a generator so that accumulators fit into the concept as well. Say, if "g" is a generator, invoking "g" with no argument will yield the same value, invoking "g" with one or more arguments, will feed these values into the generator (so that it can act as an accumulator). The only issue is about the return value. SRFI 158 dictates that generators return an eof-object when their states are exhausted. This leaves no room to return a meaningful value (as Python's generators can do so that they can act as accumulators). Another shortcoming is that the protocol of SRFI 158's generators and accumulators does not make it straight-forward to feed multiple values into an accumulator or to yield multiple values from a generator. I am not yet sure how to fix it but I do think that fixing it would make SRFI 158 better fit into Scheme's evaluation model (to which multiple values come naturally).

Marc

John Cowan <xxxxxx@ccil.org> schrieb am Fr., 13. Okt. 2017 um 01:52 Uhr:
Marc Nieper-Wißkirchen scripsit:

Adding duals of generators in form of accumulators is a great idea. However, the accumulator protocol does not look as if it is the right one:

The *protocol* is simply this:  If the argument is not an eof-object, do something with it and return an undefined value.  If the argument is an eof-object, return a value which depends in some way on the previously passed objects.  As I understand you, you are not objecting to this protocol, but to the way in which predefined accumulator constructors are specified.
 
In the current proposal, the accumulator now returns its state, meaning that the producer suddenly becomes concerned with the accumulator's state, not the constructor of the accumulator. This looks wrong.

In an accumulator constructed with make-accumulator, it returns some function of the state rather than the state itself, and the creator gets to specify the function (finalizer).    But in a prespecified constructor, the transformation done is a consequence of the way the constructor is implemented.  For example, the list-accumulator constructor's state is a *reversed* list of objects, and the finalizer is `reverse`.

Now I think your argument is that it is useful to be able to specify in advance, at construction time, whatever further finalization the user desires to have done  I think, however, that this is not necessary.  The continuation of an accumulator call with a normal object is inherently different from the continuation of a call with an eof-object. In fhe first case, the continuation will normally discard the returned value, whereas in the second case the returned value is meaningful to the caller.  

So just as the first thing to be done with a value returned from a generator is to test it with `eof-object?`, so any calls to an accumulator with a normal object will normally appear at a distinct place in the control flow from the final call with an eof-object.  As such, the last call can readily be wrapped in any finalizer that seems desirable without interfering with regular calls.  This eliminates the requirement for a double finalization, one inherent to the constructor and one supplied by the user.

I hope this helps clarify my line of thinking.

-- 
John Cowan          http://vrici.lojban.org/~cowan        xxxxxx@ccil.org
                if if = then then then = else else else = if;

To unsubscribe from this list please go to http://archives.simplelists.com