A couple of questions about generators&iterators

# Claus Reinke (12 years ago)

I do not understand why (1) iterators are specified using a self-updating API when a functional API would seem preferable, or why (2) generators are linked to functions, when block-level generators would seem to be sufficient and less complex. In some more detail:

  1. Why do iterators have an imperative API?

    As currently specified, an Iterator's next returns a {done,value} pair (ItrResult), updating the Iterator in place. A functional alternative would have next return a {done,value,next} triple, leaving the original Iterator unmodified.

    It seems a lot easier to implement the imperative API in terms of the functional one than the other way round, if one needed both. In practice, I do not see any advantage to forcing an imperative API.

  2. Why are generators linked to functions, and not to blocks?

    It is not difficult to implement a form of generators in ES5. Here is one using TypeScript for classes and arrow functions (it uses a functional Iterator API, and .then-chained expressions instead of ;-chained statements):

    definition: gist.github.com/clausreinke/5984869#file-monadic-ts-L91-L125

    usage examples: gist.github.com/clausreinke/5984869#file-monadic-ts-L492-L529

    The main source-level disadvantages of these user-defined generators wrt built-in generators are:

    (a) lack of syntax sugar for .then-chaining

    (b) no coverage of statement blocks and their built-in control structures

    (a) would be cheap to fix (monad comprehensions have been suggested here multiple times), (b) could be fixed by introducing block-level generators as built-ins.

    Block-level generators are shallower than function-level ones (continuation reaches to the end of the current block), expression-level generators are even shallower (continuation reaches to the end of the current .then-chained expression).

    This would allow us to introduce shallow generators without messing with "function" - generators would simply be another class of object that "function" allows us to write abstraction over. Composing shallow continuations could be left to user- level functions, not built-ins, so everything but the block-level generators would remain (re-)programmable.

# Tab Atkins Jr. (12 years ago)

On Thu, Jul 25, 2013 at 8:44 AM, Claus Reinke <claus.reinke at talk21.com> wrote:

I do not understand why (1) iterators are specified using a self-updating API when a functional API would seem preferable, or why (2) generators are linked to functions, when block-level generators would seem to be sufficient and less complex. In some more detail:

  1. Why do iterators have an imperative API?

    As currently specified, an Iterator's next returns a {done,value} pair (ItrResult), updating the Iterator in place. A functional alternative would have next return a {done,value,next} triple, leaving the original Iterator unmodified. It seems a lot easier to implement the imperative API in terms of the functional one than the other way round, if one needed both. In practice, I do not see any advantage to forcing an imperative API.

The imperative API is more performant in general, as it doesn't require the allocation of a new iterable object at every iteration.

(I also prefer the functional approach, but I understand the arguments against it, and am fairly comfortable with languages that use imperative iterators, like Python and PHP.)

# Brendan Eich (12 years ago)

Tab Atkins Jr. wrote:

languages that use imperative iterators, like Python and PHP.

And JS -- JS has mutation and objects. It's not going to swerve toward Haskell (sorry, Claus).

Your point about allocation is on target too.

# Claus Reinke (12 years ago)

languages that use imperative iterators, like Python and PHP.

And JS -- JS has mutation and objects. It's not going to swerve toward Haskell (sorry, Claus).

I never understood your automated dislike of Haskell. But if you are speaking for tc39 when claiming that JS has no aspirations towards supporting functional APIs when possible, that would be a serious disappointment. Personally, I believe you're selling JS short here.

Your point about allocation is on target too.

With a generational collector, short-lived allocation should be free (provided all fields need to be filled anyway). When using generators in standard iterators loop patterns, the first copy can be reused in the second iteration, and so on, so only one extra copy per loop should be needed.

Claus

# Brendan Eich (12 years ago)

Claus Reinke wrote:

languages that use imperative iterators, like Python and PHP.

And JS -- JS has mutation and objects. It's not going to swerve toward Haskell (sorry, Claus).

I never understood your automated dislike of Haskell.

You misread me pretty badly here. Why?

I never wrote anything showing "dislike" of Haskell. Rather, I said JS's future standards-based evolution is not going to swerve toward Haskell. We are not going to make extra allocations for unwanted next funargs.

I like functional programming. I'm multi-paradigm. JS is too, but not to this FP-till-it-hurts extent. Objects and functions, not functions first.

But if you are speaking for tc39 when claiming that JS has no aspirations towards supporting functional APIs when possible, that would be a serious disappointment. Personally, I believe you're selling JS short here.

Besides misreading me, your recent points have misread "deep" as a modifier to "continuation", and pushed internal iteration exclusively (which does not work in all cases as Andy said).

These errors really get in the way of your making grander claims or plans about JS!

# Brendan Eich (12 years ago)

Claus Reinke wrote:

With a generational collector, short-lived allocation should be free (provided all fields need to be filled anyway).

This is badly wrong. Nothing is free, certainly not memory use, even with great GGC. Assuming it flies in the face of evidence that developers face all the time, even on V8. Yet another problem to overcome before you sell FP-till-it-hurts...

# Claus Reinke (12 years ago)

languages that use imperative iterators, like Python and PHP. And JS -- JS has mutation and objects. It's not going to swerve toward Haskell (sorry, Claus).

I never understood your automated dislike of Haskell.

You misread me pretty badly here. Why?

Your dislike of Haskell as a reference/model for JS evolution is explicit in your quoted message. It has been obvious from earlier exchanges.

My message didn't even mention Haskell - it said that I would prefer functional APIs to complement JS imperative APIs where possible, and functional APIs if it is not possible to have both (because imperative APIs can be implemented in terms of functional APIs more easily than the other way round).

I never wrote anything showing "dislike" of Haskell. Rather, I said JS's future standards-based evolution is not going to swerve toward Haskell.

There is no technical argument in there, so it is an opinion, or a preference/like/dislike.

We are not going to make extra allocations for unwanted next funargs.

Ok, that touches on a technical argument. I remain to be convinced that the cost would be high, for the reasons I gave.

I like functional programming. I'm multi-paradigm. JS is too, but not to this FP-till-it-hurts extent. Objects and functions, not functions first.

"FP-till-it-hurts"? Why the hyperbole? I want objects and functions, not imperative first.

But if you are speaking for tc39 when claiming that JS has no aspirations towards supporting functional APIs when possible, that would be a serious disappointment. Personally, I believe you're selling JS short here.

Besides misreading me, your recent points have misread "deep" as a modifier to "continuation", and pushed internal iteration exclusively (which does not work in all cases as Andy said).

Do you now have a problem with interpreting "deep" continuations?

esdiscuss/2011-September/016484, esdiscuss/2011-October/017596

I did not push internal iteration. I am concerned about composition, abstraction, and refactoring. In the other thread, I started from the observation that .forEach does not play with yield, then I tried to re-implement the built-in for-of as a user-level function and showed some examples of abstraction being hampered.

These errors really get in the way of your making grander claims or plans about JS!

Is that necessary?

Claus

# Brendan Eich (12 years ago)

Claus Reinke wrote:

languages that use imperative iterators, like Python and PHP. And JS -- JS has mutation and objects. It's not going to swerve toward Haskell (sorry, Claus).

I never understood your automated dislike of Haskell.

You misread me pretty badly here. Why?

Your dislike of Haskell as a reference/model for JS evolution is explicit in your quoted message.

No. First, "dislike" sounds subjective and hostile, which I objected to last time. Please hear me on that.

Second, or zero'th: Haskell is a static non-strict-evaluation functional language! Your hope to push JS in that direction is misconceived on first principles. JS is strict-evaluation, full of mutation, dynamically typed. To make a case for more Haskell and less other language influence in JS's evolution, you need a principled argument that overcomes these foundational differences.

People can use JS in an FP style, refraining from mutation, using pretend-monads. That's all allowed. But it is not and will not be how the core language and library evolve, especially not at the price of extra allocations (one per iteration-step).