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

Configuration parameter

Jack-Works opened this issue · comments

In #7 (comment) @hax proposed the 3rd parameter to also accept an object to allow configuration.

let's discuss does it useful and what behavior can be configured here.

Possible usages:

  • (Implemented) step
  • custom step function (last, index) => next (Can be done with iterator helper proposal)

As #7

  • {useFloat}
    • false means force start/end/step be safe int, and use simple algorithm of i += step
    • true means use algorithms of D or E or F in #7 (comment)

As #26

  • {inclusive}
    • false for exclusive range (use < test)
    • true for inclusive range (use <= test)

And

  • {count} (or size, or length?)
    instead of specifying step, it possible to specify iteration count so
    // definitely work for inclusive float range
    Number.range(1, 2, {count: 3, inclusive: true}) // 1, 1.5, 2
    
    // possible also work for exclusive int range
    BigInt.range(10n, 20n, {count: 3n}) // 10n, 14n, 18n

I like the idea of the step parameter being provided with an entry in the options bag. In addition to making the API more extensible (without requiring everyone to fill in 1 for the step parameter before the fourth options bag argument, which would be ugly), it also makes code using the (rarer) step parameter more self-documenting.

@hax The "count" parameters can be done by:
range(start, to).take(count) // iterator helper

Yeah, I'm pretty skeptical of the options given in #25 (comment) . useFloat is just cryptic and it's not clear when you would switch on it. I want to suggest that we use an options bag, but initially, step is the only option.

range(start, to).take(count) // iterator helper

No. What I listed have a totally different semantic.

BigInt.range(10n, 20n, {count: 3n}) // 10n, 14n, 18n
BigInt.range(10n, 20n).take(3) // 10n, 11n, 12n

Yeah, I'm pretty skeptical of the options

@littledan I just listed some possibilities in my mind (like we did for many other proposals), I don't mean any of those options are urgent to add to this proposal.

Although we don't have a consense now about what options should be included in this proposal, I think it seems okay to accept an options object

- Number.range(from: number, to: number, step?: number)
+ Number.range(from: number, to: number, step?: number)
+ Number.range(from: number, to: number, options?: { step?: number })

But how to expose supported options (so that polyfill authors can decide if they need to polyfill for new options)?

if ('step' in Number?.range?.options) {}
// Like this? seems strange

@Jack-Works Yeah, this is a general problem with options bags and feature testing... You can do a test call of Number.range with an object that has a getter to see if it's invoked. This is a bit of an ongoing problem across JS and the web platform; I don't have a great answer, see also tc39/proposal-intl-numberformat-v3#1 @sffc

You can do a test call of Number.range with an object that has a getter to see if it's invoked.

Oh cool trick, I didn't know that before

Personally I would write small test like:

try { Number.range(1, 3, {step:-1}) } catch {
  // feature detected!
  return
}
// do polyfill

@hax I don't think it is a good idea to throw for unknown options.. 🤔

Please check out #27 (it won't close this issue)

Feature detection for specific options is not necessary unless a browser ships an incomplete implementation. Since Number.range() is new, just make sure you have good Test262 coverage and ask browsers not to ship an incomplete implementation. Feature detection just looks for the existence of a conforming Number.range symbol.

It is possible that we might add new options in the future after browser shiped this so feature detection might be necessary in the future

I don't think it is a good idea to throw for unknown options..

@Jack-Works
It's just an example 😆, of coz we can test for the "correct" result like:

const r = Number.range(1, 4, {step:2})
r.next()
if (r.next().value === 3)
  // feature detected!
  return
}
// do polyfill

instead of an options parameters, should we consider a mapping function?

@leobalter I don't understand; how would a mapping function meet the use cases of the options parameter?

@littledan please never mind. In a second thought I don't think I want a mapping function anyway.

instead of an options parameters, should we consider a mapping function?

I have considered things like Number.range(0, 1000, (previous, index) => next) but the use case is not clear enough so it isn't included. But happy to hear if there is some use case for it.

instead of an options parameters, should we consider a mapping function?

I have considered things like Number.range(0, 1000, (previous, index) => next) but the use case is not clear enough so it isn't included. But happy to hear if there is some use case for it.

I expect this sort of use case would commonly be satisfied using Iterable or Iterator utility functions; particularly a map function. My code makes frequent use of the following idiom

import * as iterables from "scheme:authority/path"
for (const square of iterables.map(Number.range(0, 5), number => number * number)) {
    console.log(square); // 0, 1, 4, 9, 16
}

And eventually we may have https://github.com/tc39/proposal-iterator-helpers

@hax:

No. What I listed have a totally different semantic.
BigInt.range(10n, 20n, {count: 3n}) // 10n, 14n, 18n

How is this different from BigInt.range(10n, 20n, {step:4n})?

@tabatkins The results have no difference. It just provide another way to describe the iteration. Of coz u can calculate the step from count manually but seems a little bit inconvenient (BigInt.range(start, end, {step: (end - start - 1n) / (count - 1n))

all options mentioned in this issue besides useFloats and count has been implemented.

About useFloats, please go #64, about count, please go #65.

I'll close this issue for housekeeping.