Generators vs Pipes (again)

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
2 messages Options
Reply | Threaded
Open this post in threaded view
|

Generators vs Pipes (again)

Ralph Boland
In response to my post about Pipes being an improvement over generators
Andreas quite rightly pointed out ways in which his idea of generators
is better than
my idea of pipes.  However, I still feel that pipes is very useful
generalization
of generators.

   1)  Andreas ideas of generators is really my idea of generators
(similar to iterators)
combined with the idea of coroutines.  Now I think generators without
the use of
coroutines is still very useful and I will assume  Andreas agrees.
Furthermore, I think coroutines are a great idea that every language
should have, including
Smalltalk.  In particular, as pointed out by Andreas, they are
especially useful in
combination with (my idea of) generators.  However, if Andreas is allowed to use
coroutines with generators then I should be able to use coroutines in
combination
with my pipes idea.
Revisiting my pipes idea three components but now with coroutines
available we have:
  a)  Generators (but now Andreas' idea of generators).
       Note that since my idea now includes Andreas'  it cannot be
argued that his idea
       is more powerful; one can only argue that all the extra
functionality I propose
       is not worth the additional complexity involved.
  b)  Absorbers.  Absorbers are the reverse of generators and they
similarly become
       more powerful if a coroutine capability is available.
  c)  Pipes.  Pipes provide the generator capability and the Absorber capability
       concurrently.  Pipes also become more powerful if a coroutine
capability is
       available but the coroutine capability must be more powerful
because the pipe
       may want to yield to the object feeding it data (when the pipe
is acting like an
       absorber) and may also want to yield to the object it feeds data to
       (when acting like a generator).

Clearly my argument in my original post that pipes are a very useful construct
is even stronger if coroutines are available.

 2)  In the generator software that Andreas is developing,
       I think that Andreas is combining the idea of generator and the
idea of coroutine
       into one package and that this is a mistake!
      The coroutine idea should be its own package
      (if not part of the language definition itself).
      This way coroutines may be used wherever they prove to be useful and not
      just in generators.  In particular, I hope that they are
powerful enough to be
      used in pipes both for input and output at the same time if necessary.
      For me the prototypical example of the use of coroutines is of
four players
      playing a game of cards.  As each player plays a card control is
transferred to the
      next player.  It should be natural to implement this card game
in Smalltalk using
      the coroutine package.  The pipe example though is more complex
and may need
      more thought.

 3) I don't know what a coroutine package should look like exactly. I
only know that
     with a coroutine package:
       a) It should be possible to build Andreas idea of generator, or
my idea of absorber.
       b)  It should be powerful enough to be used for Pipes
            (both input and output concurrently).  I don't believe
Andreas' generator
            package will provide this.
       c)  We should really think through what we want it to look like
before adding it.
            Perhaps ideally it should be supported by syntax and even
if we cannot now
            change the syntax to support coroutines we should
acknowledge the ideal.

  4) As pointed out in my original posting,  my idea of pipes is
really stolen from the
      (Bash in my case) Linux shell.  In Linux, pipes are used between
processes and
      thus, since Smalltalk supports multiple processes (threads),  a
full implementation of
      Andreas' generators or my pipes should include their use in
communicating between
      Smalltalk processes (threads).

   5) Apart from Andreas' very useful feedback no one provided any
feedback whatsoever.
       If my idea is a good one it would be nice to hear.
       More importantly if it stinks telling me and others why is very useful.
       Better to be sent back to the drawing board than to be left in the dark.
       Lastly, I doubt my ideas are the last word on pipes.  Ideas
from others on how to
       improve upon it would be the most useful comments of all.  Are
my ideas so out
       of sync with the Squeak community that they are completely ignored,
       save for Andreas?

My original posting was long so I have not repeated it here.  It
should be easy to find in
the archives and I can forward a copy to anyone who cannot find it.
Andreas'  (first) response to my posting follows:

> > I do not understand all the commotion over generators.

> Generators transform a push model (#do:) into a pull model (#next).
> Let's say you have two algorithms one that produces values via a push
> model and one that consumes values via a pull model:

> producer := SomeAlgorithm new.
> producer do:[:value| "produces the value from the algorithm"].

> consumer := AnotherAlgoritm new.
> consumer process: inputStream. "consumes the value from inputStream"

> Plugging these two together can be a royal pain. You basically have the
> following three options:

> 1) Preproduce all the values from the producer and create a stream that
> holds them:

> values := Array streamContents:[:s| producer do:[:each| s nextPut: each]].
> consumer process: values readStream.

> This can work very well if the number of values is limited and if you
> know you're going to consume all the produced values (doesn't work that
> well with infinite sequences of numbers).

> 2) Rewrite either one of the algorithms. Can work very well if it's your
> own code and reasonably simple, and can be extremely painful if it's
> complex.

> 3) Use a Generator. With a generator, the solution becomes simply:

> consumer process: (Generator on:[:yield| producer do: yield]).

> When you can neither use solution 1) or 2) generators simplify your life
> *greatly*. In fact I'd say they are indispensable if you're dealing
> with a complex existing environment.

> > If a Collection class cannot do what you want, use a Stream.
> > Take a read only Stream and subclass from it a class called GenMyData.
> > Inside GenMyData write code that generates the data you
> > need on the fly and feeds it to the user upon request using
> > the ReadStream interface.
> ...
> See above. You're describing option #2. Rewriting an existing algorithm
> to "feed" the data properly can be decidedly non-trivial. You can start
> with Integer>>primesUpTo:do: which is a very simple algorithm. The point
> being that while all algorithms can be expressed either way the generic
> way in which a Generator does that replaces the need to re-implement each
> specific algorithm.

> Cheers,
>  - Andreas





--
Had a wife and kids in Florida, Jack (Nicklaus)
Went out for a "ride" but I couldn't get back
As a ten timer being hunted down
OR
When the wife found out who I was knowing (biblically speaking)
I hit a hydrant and I just kept going (till I hit a tree).
...

Reply | Threaded
Open this post in threaded view
|

Re: Generators vs Pipes (again)

David T. Lewis
On Sat, Feb 13, 2010 at 11:46:23PM -0700, Ralph Boland wrote:
>
>   4) As pointed out in my original posting,  my idea of pipes is really stolen from the
>       (Bash in my case) Linux shell.  In Linux, pipes are used between processes and
>       thus, since Smalltalk supports multiple processes (threads),  a full implementation of
>       Andreas' generators or my pipes should include their use in communicating between
>       Smalltalk processes (threads).
>

I don't have anything to say one way or the other about generators,
coroutines, etc. But I do agree with your point that the concept of
pipes is quite useful. As a minor correction, I have to point out
that the concept has nothing to do with Linux, it was one of the
fundamental ideas that Unix got right very early on in more or less
the same time frame that Smalltalk was being invented.

Your original posting put forward quite a few interesting ideas, but
taken down to the basics, it seems to me that if you just implemented
the read and write stream protocols (which IIUC is the fundamental idea)
on top of a SharedQueue, then you have a pipe.  You can imagine all
kinds of variations (e.g.  pipes between images, various performance
optimizations, and so forth), but read and write streams on a SharedQueue
should cover the basics with very little new invention required.

In the CommandShell package (which loosely emulates a unix shell with
a perly mashup of Smalltalky things) I used character-oriented OSPipe
and InternalPipe to implement unix style pipe behavior. I always
thought it would be a good idea to replace this with a generalized
pipe of objects (not just characters), and use SmartRefStream to
convert objects to external form when sending them through an OSPipe.

So FWIW I like your suggestion of adopting the concept of pipes in
Smalltalk.

Dave