Iterables vs arrays for tuples in zip function
ivan-kleshnin opened this issue · 7 comments
zip
most common usage is to combine values with their indexes
Now we have to deal with generators, inside newly created kinda-tuples.
Generators are still broken in Node6. Take console.log
:
// nope
console.log(zip(["a", "b", "c"], range(1, Infinity))) // {}
// nope
console.log(Array.from(zip(["a", "b", "c"], range(1, Infinity)))) // [{}, {}, {}]
// yep
console.log(Array.from(zip(["a", "b", "c"], range(1, Infinity))).map((x) => Array.from(x))) // [["a", 0]...]
Too much pain for a normal workflow IMO.
So I think it would be practical to use basic arrays for those tuples.
At least until some serious tech update will change the status quo.
For a sidenote: perfect console.log
should output, say, first 10 items of an iterable printing ...
if there are more.
Hey, this is really interesting, I've had quite a think about it. On the one hand I guess I was trying to avoid using arrays for anything and to keep everything as lazy iterables, but on the other hand if this makes things hard to work with maybe there's no real loss from using length 2 arrays to represent the tuples.
I did have another thought around what you are saying about debugging and logging, what do you think about defining a custom toString
method for the iterables so your ideal string output could be achieved simply by coercing the iterable to a string?
In the source there's a genToIter
function which accepts a generator and creates an iterable, it could be modified along these lines:
const genToIter = gen => {
const iter = {[Symbol.iterator]: gen}
Object.defineProperty(iter, 'toString', {value: () => /* return ideal representation */})
return Object.freeze(iter)
}
Then in theory we could just do:
console.log(String(zip(["a", "b", "c"], range(1, Infinity)))
// => [["a", 0], ["b", 1], ["c", 2]]
It's a shame it will still need to be coerced to a string, but let me know what you think of that idea :)
Sounds decent. Though there're more pitfalls.
Other common case for zipping which breaks is indexing
pipe(
zip(firstGen, secondGen),
map(([firstItem, secondItem]) => { // destructuring requires Array
...
})
)
I guess until Proxy
we can't substitute it.
Ah no, you can destructure an iterable, your code will run fine :) I'll have a think about how best to represent iterables for debugging purposes
Added a PR here #13
It's a bit noisy because there's some renaming, but I've decided to go with the following:
String(range(1, Infinity)) // => '(1 2 3 4 5 6 7 8 9 10...)'
String(zip([1, 2, 3], range(1, Infinity)) // => ((1 0) (2 1) (3 2))
I've used a LISP-like notation so iterables aren't confused with JS arrays. I'll update the docs too before doing a release. Do let me know if you have any thoughts!
Ah no, you can destructure an iterable, your code will run fine :) I'll have a think about how best to represent iterables for debugging purposes
I wanted to say we can't use indexing. My inference about destructuring was a mistake.
let [x1, x2, x3] = (function* () { yield 1; yield 2; yield 3 })()
console.log(x1) // works
let y1 = (function* () { yield 1; yield 2; yield 3 })()[0]
console.log(y1) // not
Added a PR here
Thank you. I'll check it out. Though I don't see a value in total lazyness
I really care the most about usability and only practice reveals that.
Hi
Though I don't see a value in total lazyness
In this case maybe does not seems that has value. But imagine that a permutations method is implemented and you want only a slice of permutations:
const ps = permutations([1, 2, 3, 4]);
const iterable = slice(10, 15, ps)
const array = [...map(e => [...e], iterable)]
If it is total lazy you don't need to calculate the first 10 permutations. You can even do permutations of an infinite range:
permurations(range(0, Infinity))
I've implemented this library that currently zip
, cartesian
, permutations
return iterable of arrays but following example of this library I think that I will implement total lazyness because has advantages.
I'm going to close this as I think it's more consistent and also more useful to return nested lazy iterables everywhere rather than lazy iterables of arrays under some circumstances and I think destructuring solves the problem of easily accessing values after using zip