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 forcestart/end/step
be safe int, and use simple algorithm ofi += 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}
(orsize
, orlength
?)
instead of specifyingstep
, 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)
)