Single-bounded `range` as default


#1

I want to propose to make the standard range function only take one parameter, end, and derive start from Zero::zero, like Python allows too (though they have a signature like range([start,] end, [step])).

This is the most common case, 5x as often as the variant with start != 0, at least for the compiler and the standard libs. I myself haven’t needed the start parameter once so far.

In addition to being more ergonomic to use (IMO) this also helps for custom indexing types.

for i in range(upper_bound()) {}

as opposed to

for i in range(CustomIndexType(0), upper_bound()) {}.

Of course we should still retain the current range function, perhaps under a different name.


#2

With an absence of optional function arguments (which is another topic for its own), you won’t be able to override range in that fashion. But we already have a precedent of slice, slice_to and slice_from and we may adopt that convention to range:

for i in range_to(upper_bound()) {} // counting up to upper_bound()
for i in range_from(42u) {} // counting upwards starting from 42

I’m not sure if they will be absolutely useful enough to admit the inclusion to std::prelude though. (For comparison, range_inclusive is not in the prelude.)


#3

Yea, as I wanted to make clear in the first post, I don’t want to overload the function, I want to replace it and give the current function a different name.

In the first post I tried to make clear why I think that the current range function in the library has way less usage once one would include a standard range to count from Zero::zero:

Quick grep statistics showed me a ratio of about 1 usage of range with both parameters to 5 usages of range with start being Zero::zero of the relevant type.

Note also that there is an overproportional usage of the double-parameter range in the tests, where it’s mostly just used to produce nicer looking numbers.

EDIT: The exact statistics:

$ grep -r '\<range\>([^0]' src/lib*/ | wc -l
78
$ grep -r '\<range\>(0' src/lib*/ | wc -l
397
$ python -c 'print(78/397)'
0.1964735516372796

#4

I’ve just checked my code, and I observed the same 5:1 ratio of range with a starting zero vs not (135 vs 27). I’d be fine with a function to simplify the common case, but it has to be shorter than the current solution. In particular, range_to is not.

As always, a macro could be used in lieu of language support for optional arguments:

range!(stop) -> range(Zero::zero(), stop)
range!(start, stop) -> range(start, stop)
range!(start, stop, step) -> range_step(start, stop, step)

#5

Since we spend so much more time reading than writing code, I’m wary of any changes that make writing faster, but reading harder. Perhaps for Python programmers, range(9) is instantly recognized, but certainly range(0, 9) is explicit and leaves no doubt to any reader.


#6

is explicit and leaves no doubt to any reader.

Except, of course, whether the last argument is inclusive or not.

I don’t think there’s an argument here that the single argument range is harder to read that doesn’t rely on the existing familiarity with the two argument version of range.