## Re: Generators and coroutines

Expand Messages
• ... the ... function ... the ... can ... two ... Just curious here, but if a, b and c are streams, how does a + b followed by a + c evaluate (probably in the
Message 1 of 35 , Nov 1 4:18 AM
• 0 Attachment
--- In langsmiths@yahoogroups.com, James McCartney <asynth@i...>
wrote:
>
> On Oct 28, 2004, at 12:21 PM, Daniel Ehrenberg wrote:
>
> > Adverbs are really just J's way of looking at
> > higher-order functions,
>
> yes but they are a bit more than that. adverbs are polymorphic on
the
> functions they modify. There is an adverb for the inverse of a
function
> for example.
>
> > and it's much simpler to just
> > make functions first class, as I believe Wrapl has
> > done anyway.
>
> > But how would it work to have a higher
> > order function that specifies order of evaluation in a
> > strict language? I don't see what's so terrible about
> > the curly bracket syntax anyway.
>
> It doesn't do what I am asking for because it forces me to write
the
> code for pulling those streams (i.e. generators).
> In my SuperCollider language which has a limited form of adverbs I
can
> write:
>
> a + b
>
> if a and b are streams then this is the equivalent of adding the
two
> streams in parallel.
> length(a + b) = min(length(a), length(b))
>

Just curious here, but if a, b and c are streams, how does

a + b
followed by
a + c

evaluate (probably in the obvious way I'm thinking).

a + a

?

> if I write with the 'across' adverb x:
>
> a +.x b
>
> it does addition of every element from stream a to every element of
> stream b.
> length(a +.x b) = length(a) * length(b)
>
> This works for every binary operator. I can write new operators and
> this will work without adding code to take it into account. With
the
> curly bracket syntax, every function that used it would have to
pull
> the generators explicitly.
>
>
>
> --
> --- james mccartney james@a... <http://www.audiosynth.com>
> SuperCollider - a real time audio synthesis programming language

In my opinion it would be easier to allow operator redefinition
(Wrapl uses multiple dispatch for this). Then you could define the
addition operator for streams to do exactly what you want. In
to behave in either of the two manners you have above. At the moment
Wrapl doesn't support coroutines since it's not really finished
anyway, but it will (probably using some sort of lightweight
cheating really, A op B will be translated into op(A, B)...).

Then one can write
def stream is <C> \$; -- defines a simple structure/record

+[stream][stream] is <X, Y> (
stream{rep susp @X.C + @Y.C}; -- return a new stream
);

+.[stream][stream] is <X, Y> (
var _X;
stream{rep (
_X <- @X.C // leave;
rep susp _X + @Y.C;
)};
);

Now

stream{1 to 3} + stream{1 to 4}

should return a stream which will produce 2, 4, 6 and then fail
whereas

stream{1 to 3} +. stream{1 to 4}

should return a stream which will produce 2, 3, 4, 5, 3, 4, 5, 6, 4,
5, 6, 7 and then fail.
Now I would still need the braces to pass a coexpression to stream.

Also I would have to redefine most of the basic operators to allow
streams and normal values to be mixed together, and I see this
getting very messy already, but all this crap can go into a single
seperate module; the user of the module would only have to call stream
{<generator>} every now and again to create a stream.

Right now I'm thinking of making this module the builtin behaviour of
coexpressions, heh heh heh...
• ... Hmm, maybe the right choice is to hide exhaustible generators behind uglier names, making the default names like ReadLinesFrom file and
Message 35 of 35 , Nov 10 10:02 AM
• 0 Attachment
Marcin 'Qrczak' Kowalczyk <qrczak@...> writes:

> Only some generators are truly exhausted (e.g. reading from a file).
> Others will merely perform a pure computation again (e.g. arithmetic
> sequences).

Hmm, maybe the right choice is to hide exhaustible generators behind
uglier names, making the default names like 'ReadLinesFrom file'
and 'DirectoryContents dir' return lazy lists instead.

Generators are used for transient data structures between
transformations, to process data lazily and avoid materializing
all data at once, e.g. if you write:

let array = GenMap someList (From 0) ?elem index {
someComputation elem index
}->Reject somePredicate->Array;

then memory usage will be small if most elements are rejected,
and will usually be constant if you replace Array with Sum.

But if you replace Array with some function which traverses its
argument multiple times, then someComputation and somePredicate will
be called multiple times for the same elements. Whether this is
correct or not, depends on what they do. Usually it is correct,
might only be inefficient. So I think most generators are safe when
evaluated multiple times, so it's better to avoid using them for
reading files (unless we know that it's processed once right away)
and code can generally assume that they are safe.

--
__("< Marcin Kowalczyk
\__/ qrczak@...
^^ http://qrnik.knm.org.pl/~qrczak/
Your message has been successfully submitted and would be delivered to recipients shortly.