wren-lang / wren

The Wren Programming Language. Wren is a small, fast, class-based concurrent scripting language.
http://wren.io
MIT License
6.9k stars 552 forks source link

[RFC] Enhancements to Range class #913

Open PureFox48 opened 3 years ago

PureFox48 commented 3 years ago

Although ranges in Wren are good they could, IMHO, be made better in two respects.

Firstly, there should be a way to specify the step which currently is always one. For example:

for (i in (0..10).by(2)) System.write("%(i), ")
// would print 0, 2, 4, 6, 8, 10

To avoid potential confusion, I think the step should always be positive whether the range is ascending or descending.

Secondly, an occasional bug in my own code is that I'm sometimes surprised (when the start or end point of the range is variable) that the range is iterating in a different direction to what I intended! It would be useful if there were a way to 'lock' a range to always iterate in a particular direction so that it would be empty if the boundary points evaluated to being the wrong way around. So we'd then have something like this:

var a = 0
// ....
for (i in (1..a).asc) System.write("%(i), ")
// no output as 'a' is less than '1' and the range is locked to ascending

for (j in (10..a).by(2).desc) System.write("%(j), ")
// would print 10, 8, 6, 4, 2, 0 as the range would be descending anyway

Incidentally, the proposed method names here (by, asc and desc) are just ones which immediately come to mind - I'm not particularly wedded to any of them.

Now, I know that you can always use a while rather than a for statement to get around these problems - and I do - though I then sometimes forget to increment the 'control' variable and end up in an infinite loop :)

I've also written library classes which wrap a range and then change the way the iteration works though, as this is a rather clunky solution, I don't use them much.

Other languages often have built-in (and sometimes rather cryptic) syntax to deal with steps and iteration direction though I see no need for this in a resolutely object-oriented language such as Wren. Additional methods would be fine.

As always, I look forward to comments.

mhermier commented 3 years ago

Excluding the fact that it can break existing code, I find it not really elegant mainly because it is some kind of method chaining...

ChayimFriedman2 commented 3 years ago

What's bad with method chaining?

PureFox48 commented 3 years ago

@mhermier

Excluding the fact that it can break existing code

How can it do that?

I thought that it wasn't recommended to inherit from built-in classes (apart from Sequence) so, even if it works, you shouldn't be doing it anyway.

I find it not really elegant

Well, it's always more elegant to use built-in syntax but, personally, I don't find the use of methods instead unattractive.

But anyway, however it's done, what about the general principle of these proposed changes. Do you think they're worthwhile or not?

mhermier commented 3 years ago

There is nothing wrong with method chaining, the problem is that the lack of a method chaining operator, make the things "ugly". If we have an operator for method chaining, lets say ; then the example become something like:

var a = 0
// ....
for (i in 1..a;ascending = true) System.write("%(i), ")
// no output as 'a' is less than '1' and the range is locked to ascending

for (j in 10..a;step = 2;ascending = false) System.write("%(j), ")
// would print 10, 8, 6, 4, 2, 0 as the range would be descending anyway

There is an ambiguity of priority that needs to be solved probably with parenthesis but this makes setters/getters all unified and allow to tune the chained object one the go.

mhermier commented 3 years ago

@PureFox48 I thought the method you added was changing some existing behavior, my bad if you added new names.

PureFox48 commented 3 years ago

@mhermier

I don't dislike your idea of using ; as a method chaining operator and it's what I would have suggested myself if we were to change the language syntax rather than just adding extra methods.

However, it's clearly a more far reaching idea and, even if we had it, we'd presumably still need to add new methods to the Range class in the first place.

PureFox48 commented 3 years ago

I've been thinking some more about the issues raised here (which for me, at least, are a constant thorn in the flesh) and have come up with what I think is a more elegant solution (clearer, less parentheses) than using instance methods.

Suppose instead we give the Range class two constructors:

Range.asc (start, end, step)
Range.desc(start, end, step)

where :

start is the start point of the range (inclusive)

end is the end point of the range (also inclusive)

step is the step value (a positive integer).

Range.asc and Range.desc would always produce ascending or descending ranges, respectively. If start and end were the wrong way around, the constructors would simply return empty ranges.

Although these constructors wouldn't support exclusive ranges explicitly, they are of course easy to simulate by adjusting the end point appropriately.

An instance getter (step) would also be needed to return the step value.

So the earlier examples would now look like this:

for (i in Range.asc(0, 10, 2)) System.write("%(i), ")
// would print 0, 2, 4, 6, 8, 10

var a = 0
// ....
for (i in Range.asc(1, a, 1)) System.write("%(i), ")
// no output as 'a' is less than '1' and the range is locked to ascending

for (j in Range.desc(10, a, 2)) System.write("%(j), ")
// would print 10, 8, 6, 4, 2, 0

System.print((1..6).step) // 1

I did wonder about making the end point exclusive as this would be more consistent with existing APIs such as Random.int and with what some other languages do (c.f. Python). However, arguably inclusive ranges are more intuitive for users particularly for descending ranges.

The present syntax for constructing ranges using the .. and ... operators would be unaffected and would therefore continue to work the same way it always has.

mhermier commented 3 years ago

No particular opinion. Seems plain boring, but don't know it is a good or bad thing though.

PureFox48 commented 3 years ago

Seems plain boring

It's a good thing :)

It implies that it doesn't take much learning.