tc39 / proposal-iterator.range

A proposal for ECMAScript to add a built-in Iterator.range()

Home Page:https://tc39.es/proposal-iterator.range/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Inclusive range

hax opened this issue · comments

Current proposal only cover exclusive range, I'm wondering whether we could add inclusive range support by overloading the third parameter:

Number.range(1, 5) // 1, 2, 3, 4
Number.range(1, 5, 2) // 1, 3
Number.range(1, 5, {inclusive: true})  // 1, 2, 3, 4, 5
Number.range(1, 5, {step: 2, inclusive: true}) // 1, 3, 5

Another possibility is using a separate method:

Number.inclusiveRange(1, 5)  // 1, 2, 3, 4, 5
Number.inclusiveRange(1, 5, 2) // 1, 3, 5

Possible range literal (coming from slice notation, note I'm not a big fan of literal for ranges, just throw the idea):

(1:5) // Number.range(1, 5)
(1~5) // Number.range(1, 5, {inclusive: true})
(1:5:2) // Number.range(1, 5, 2)
(1~5:2) // Number.range(1, 5, {step:2, inclusive: true})

Note (1:5:2) and (1~5:2) is not symmetry, so maybe we could change them to (1:5; 2) and (1~5; 2)

Can't you build this very easily by just appending the end value onto the result of Number.range?

In particular, if the iterator helpers proposal (or a follow-on) added a "concat" operation, it'd be trivial to do something like Number.range(x, y).concat([y]) - otherwise i think you'd need a generator like function* range(x, y) { yield* Number.range(x, y); yield y; }, which isn't particularly ergonomic.

Or range(x, y + step, step) to get a inclusive range

Yeah, it's just about ergonomic. I believe that's why many other languages (ruby, groovy, swift, kotlin, etc.) support both exclusive/inclusive ranges. And for those not have built-in inclusive range, there are always stackoverflow questions (try search "python inclusive range" 😂)

but isn't it weird that only one of the boundaries is getting added to the range?

This is how I see it working at present.

[...Number.range(2,5)]
 [3, 4, 5]

I mean why not 2 if we are adding up 5?

default could have been full inclusive.

I would expect [2, 3, 4].

@thecodejack
For range(2, 5), I think no one expect 3, 4, 5. The possible expectations are 2,3,4,5 (inclusive range) or 2,3,4 (exclusive range). Some languages provide both (with different syntax), some only provide exclusive range (python).

Which one matches .substring?

I'm sorry the current spec (and polyfill) have two bugs. I accidentally inverted the inclusive and exclusive on the start and end. Please checkout #27

@thecodejack

An inclusive range feels like overkill to me, when you can adjust the end value instead.

I would personally be very surprised if I ran the following code and didn't see the follow logs:

let str = "abc"
for (let i of Number.range(0, str.length))
    console.log(str[i]);
// logs "a", "b", "c"

Yeah, lots of patterns lead you to the inclusive-exclusive range; anything else would make a lot of things bad. range(0,5) absolutely has to be [0,1,2,3,4].

I would personally be very surprised if I ran the following code and didn't see the follow logs:

let str = "abc"
for (let i of Number.range(0, str.length))
    console.log(str[i]);
// logs "a", "b", "c"

@kmiller68 I agree with this. IMO, working with the length property is the only reason that makes me not want a fully inclusive range method. I don't want to force a o.length - 1 case.

An inclusive range feels like overkill to me, when you can adjust the end value instead.

@littledan can you extend why is it overkill? I can't see this way and I'd like to understand it a bit more.

Yeah, lots of patterns lead you to the inclusive-exclusive range; anything else would make a lot of things bad. range(0,5) absolutely has to be [0,1,2,3,4].

@tabatkins My challenge is how do we name the second parameter?

The current proposal says:

Number.range( from , to, step )

I read the parameters as:

  • start from a given number.
  • up to a given number.
  • skipping step here

It's hard for me to describe that we start from a number and set the range until the number on the second parameter.

We should probably draft a better signature for this function.

btw, Lodash's range is inclusive-exclusive.

I believe it's a good model to follow, unless we have a very strong to make things differently.

Number.range(1, 4) returning [1, 2, 3]

I think everyone's agreeing with the spec text and polyfill that, by default, we should be inclusive of the lower end and exclusive of the higher end, as @leobalter and @kmiller68 are suggesting. See #27 for the bug fix confirming this intention. The issue is about whether we should add an option to be inclusive of both ends. I don't understand under what situations you'd need this setting to be inclusive of both ends, given that you can just add 1 (or the step value) to the end to get the inclusive-inclusive semantics.

Thank you @littledan to clarify the issue.

I'm sorry that I use "exclusive range" which really mean "inclusive-exclusive", and "inclusive range" which really mean "inclusive-inclusive", which may cause confusion. I use these terms as it seems how python guys use.

I don't understand under what situations you'd need this setting to be inclusive of both ends

Personally I'm ok with only exclusive range. But I notice that there are always people expect inclusive-inclusive ranges and many other programming languages provide both (and at least Kotlin only provide inclusive range syntax a..b, and exclusive range a until b seems secondary). I guess it's mainly for completeness and ergonomic.

For example, Ruby range provide both (inclusive and exclusive), and, the signature of Range.new is new(begin, end, exclude_end=false), aka. Range.new(1, 10) create an inclusive range by default 😆

@Jack-Works

Thanks for fixing it.

[...Number.range(2,5)]
 [2,3,4]

is good for me

Implemented in #32