tc39 / proposal-iterator.range

A proposal for ECMAScript to add a built-in Iterator.range()
https://tc39.es/proposal-iterator.range/
MIT License
487 stars 13 forks source link

What to do when the direction mismatch #5

Closed tiansh closed 4 years ago

tiansh commented 5 years ago

I would personally prefer the behavior in Python language: Number.range(1, 10, -1) would yield []. This should be expected behavior IMO. And user may ignore the checking of whether from is less than to before invoke Number.range.

Jack-Works commented 5 years ago

Thanks, another solution to direction mismatch.

You can create a pr for it, or I'll add it to the proposal later.

Jack-Works commented 5 years ago

Already updated the proposal.

tabatkins commented 4 years ago

Strong agree with this, btw. It's the friendliest behavior, and in my opinion has the most consistency.

Infinite looping is definitely the worst option. You never want to infinite-loop by accident, and we already have semantics for getting an infinite range on purpose. It's not even clear how one would implement this behavior without causing it on purpose; the only naive way to hit it, afaict, is by stopping the loop with an exact equality test, which would also infinite loop for plenty of normal arg combinations.

Throwing isn't a good option; my personal experience says that it's fairly easy to get wrong-order arguments by accident, especially when the args come from user input. Having to guard against that all the time isn't great, when we've got better options below.

Ignoring the sign of the step is a reasonable possibility. It implements the semantic of "every Nth value, starting from A and ending at B". This sounds reasonable, but in my experience it's actually too clever; if I'm giving an explicit step, I almost always know what direction I want the range to go in, and implicitly reversing it would cause problems.

Giving an empty range is the best option in my opinion. It implements the semantic of "every Nth value in the chosen direction, starting from A, that isn't larger/smaller than B". Writing out this semantic takes more words than the previous, but it's actually a real simple mental model. It gives us the most reasonable limit behavior too, I believe - in range(A, B, 1), if you hold A steady and gradually decrease B, the resulting range has less and less items in it, until A==B and it's empty. Further decreasing B should keep it empty; none of the values it can produce are less than B.

It's also the behavior you'd get with the most obvious implementation that didn't do anything clever at all: for(let val = A; val < B; val += C) yield val;. Simple and easy to understand.

(That said, if C is omitted, it should depend on the relative ordering of A and B, so you will get an increasing or decreasing range according to the numbers you used. This, too, matches expectations the most in my personal experience.)

Jack-Works commented 4 years ago

Will add a PR later to use the yield nothing semantic since it seems like there is no one prefer other semantics than this currently.