leebyron / ecmascript-iterator-hof

Higher Order Functions on Iterators
42 stars 2 forks source link

Add collect method #5

Closed edevine closed 7 years ago

edevine commented 7 years ago

Consider this snippet in the readme which incorrectly suggests the return value of flatten is an array.

var deepValues = [ [ 'A' ], [[ 'B' ]], 'C' ]
var flat = deepValues.values().flatten() // [ 'A', ['B'], 'C' ]

Adding the method collect:

class Iterator<T> {
    collect<R>(fn: (iterator: Iterator<T>) => R): R;
}

Would allow:

var deepValues = [ [ 'A' ], [[ 'B' ]], 'C' ]
var flat = deepValues.values().flatten().collect(Array.from) // [ 'A', ['B'], 'C' ]
leebyron commented 7 years ago

I can update the Readme to make it more clear that the return values are Iterators.

I'm not sure I see the value of collect() isn't iter.collect(fn) equivalent to fn(iter)?

edevine commented 7 years ago

It is, but if we're dealing with several chained methods with line breaks, collect would make the code much more readable.

deepValues.values()
  .filter(value => value.isValid)
  .map(value => value.children)
  .flatten()
  .collect(Array.from);

vs

Array.from(
  deepValues.values()
    .filter(value => value.isValid)
    .map(value => value.children)
    .flatten()
);
edevine commented 7 years ago

The method could also attempt to construct the callback to be used with Map and Set:


Iterator.prototype.collect = function (collect) {
  return Symbol.species in collect ? new collect[Symbol.species](this) : collect(this);
}
leebyron commented 7 years ago

I don't think all current and future uses of Species will accept that call signature

RangerMauve commented 7 years ago

Are there any examples in the wild that use a similarly named method?

edevine commented 7 years ago

@leebyron No they won't. Neither will all functions accept the call signature either. It's up to the developer/ static type checker to know when to use them.

edevine commented 7 years ago

@RangerMauve I came across the name in these meeting notes: https://github.com/rwaldron/tc39-notes/blob/master/es6/2014-11/nov-19.md#58-mapprototypemap-and-mapprototypefilter-spec--set

My suggested implementation is different, but the essence is the same: a convenient API to put iterator results back into a collection after they have been transformed.

leebyron commented 7 years ago

I'd like to keep this proposal limited to just those higher-order operations on Iterators themselves to keep a well defined scope for an eventual proposal.

I was present for that particular conversation and remember the controversy around a collect method and introducing a collection prototype. The consensus there was to start with an Iterator prototype proposal, which inspired work on this.

If work begins on some kind of shared collection prototype, then perhaps that would be the right opportunity to define a protocol for creating collections from Iterators. Right now Symbol.species might not be the complete solution.

edevine commented 7 years ago

@leebyron How about a more general purpose infix method such as D3's selection.call() for chaining arbitrary functions.

class Iterator {
  call(callback, ...args) {
    return callback(this, ...args);
  }
}