-
-
Notifications
You must be signed in to change notification settings - Fork 2.8k
change switch range syntax to be more clear and perhaps also allow exclusive ranges #359
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
Since the for over a range is under consideration, I just want to think out loud a bit.... using the two different range operators allowed in both places:
If we're considering python style array slicing could we do negative indices:
Could we iterate backwards?
And finally, could we specify a stride/step?
This only really makes sense if we're able to do the same thing for the for over a range:
|
I think having both We couldn't do the backwards or stride, because a slice only creates a pointer and a length; it does not copy data around. As for negative... it seems simpler to require a |
Another problem with negative indexes is that if the compiler doesn't know at comptime if an index is positive or negative, it would have to emit a conditional branch, which sounds like a bad idea. If there was going to be a way to index backwards, it would need to be comptime unambiguous. |
Yeah, the python style slicing is not appropriate. Doing step/direction in the for wouldn't require the copying around as it would just be a while loop with a counter, but the point here is to make the syntaxes consistent and it makes things less simple not more... So yeah, disregard the above... The other only other thought on this subject I wanted to share is specifying a range not with start and end, but start and number of elements... ie:
|
I think it's reasonable to want to have a start and a length rather than start and end. But I think there's value in the language having a single convention. |
Yeah, this is purely syntactic sugar and completely unecessary. Consider the following:
|
I see your point here, but I question this assertion: for (a ... b) | x, i | { } // Fails if a > b I think this would simply iterate 0 times, the same way that this would: var i: usize = 100;
while (i < 10; i += 1) {} As for the other one: for (arr[a ... b]) | x, i | { } // Fails if a > b OR b > arr.len OR a > arr.len Because of the transitive property, we only have to compare |
Good point, sounds reasonable. You'd need to check the second case anyway. |
Now slicing syntax is |
..
and ...
syntax
Looks good! |
It should be said that this syntax is the exact opposite of what Ruby does: https://ruby-doc.org/core-2.1.5/Range.html Not that Ruby should dictate Zig, but it's very unfortunate. But the syntax is very clear though. I think it's hard to see the difference clearly. I had a related comment here: #358 (comment) |
I think having both .. and ... will lead to lots of bugs.. |
I updated the OP to clear up confusion. Ruby's syntax is nuts. How could more dots mean less numbers in the range? The mnemonic is completely backwards! |
I like @thejoshwolfe's suggestion. The This also resolves the question of wether Switch on floats could be nice. I think only Maybe it looks a bit ugly to some, and a few more characters to type, but it's easier to read unambiguously |
Yesterday I different switch range usecase came up: I wanted to switch on type and have a case for |
@daurnimator you already can switch on size of integers, just like that: switch (@typeInfo(arg).Int.bits) {
0...63 => //
64...65535 => //
} |
I propose this syntax: switch (c) {
5 -> 10 => {}, // exclusive, another variant: a ~~ b
'a' ->+ 'z' => {}, // inclusive, another variant: a ~~+ b
} |
A tiny suggestion: If it's decided that both I feel like the difference between two and three dots is small enough that there will be hard-to-find typo bugs, similar to the classic Four dots would stand out clearly. |
In python you can write
Which is very intuitive to understand. It's more flexible too because you can use it outside of switch statements as well. Also, in python you can chain more than one expression: ie |
I think it is very important to remember, that range bounds may be constants defined somewhere else, so all this +1/-1 may just confuse and make code less readable. Adding my 2 cents to @thejoshwolfe proposal.
maybe even
|
In Odin, there were many options to go for iff I wanted to unify slicing operations and ranges. However, I decided not to unify them and keep them as different concepts because they are fundamentally different ones too. The act of slicing is different to indexing with a range, you can treat them as if they were the same, but they are actually different things conceptually. array[lo:hi] // slicing syntax, [lo, hi)
case a ..< b: // range syntax [a, b)
case a .. b: // range syntax [a, b] If you wanted to unify these conceptions, these are the possible solutions:
The first approach is the most confusing for two reasons, the things are not that distinct in their appearance and they can have the opposite meanings in different languages e.g. Ruby vs Rust. For Odin I settled on the third approach because it's probably the clearest view in my opinion. |
I also wanted to give my 2 cents, I like how Raku handles this: https://docs.raku.org/type/Range switch (x) {
0 ..^ 5 => {}, // 0, 1, 2, 3, 4
5 .. 10 => {}, // 5, 6, 7, 8, 9, 10
10 ^..^ 15 => {}, // 11, 12, 13, 14
14 ^.. 20 => {}, // 15, 16, 17, 18, 19, 20
} |
@ManDeJan In Nim, the caret is used to be shorthand to mean from the end. This it the problem with choosing syntax. Every other language chooses it differently. |
My proposal?
my feeling now: so my real proposal:
never mind those crazy ideas about float range, enum range and enum-indexed array/tuple... |
I am against using |
Can we have a reason as to why this has been closed? |
status quo:
the two symbols are different looking closing this issue doesn't preclude the possibility of changes, but the wording of this issue's title and the lack of concrete proposal here mean that this discussion is not actionable. if a change is to be considered, it should be a separate issue with a concrete proposal. |
I quite agree that the status quo is fine. Having both Using brackets for intervals would look neat, but once again:
I don't see any reason to ever use that over |
I think it is actually more complicated to have to remember the rules It is unfortunate that there already exist inconsistencies in how other languages assign the semantics, but again, I would personally much rather remember which is which, and use any of them as I see fit, than remember only one is for slices and only one is for switches. Summary: please take this as my personal opinion that this issue should be reopened. Cheers! |
I have been appreciating zig's preference to keywords over operators (especially control flow keywords). Operators may be heavily overloaded with incompatible meanings in different programming languages, but keywords are less likely to cause confusion. So i have come up with this idea: switch (rng.getRandomPercent()) {
0 upto 30 => std.debug.warn("Choice A"), // 0-29
30 upto 70 => std.debug.warn("Choice B"), // 30-69
70 upto 100 => std.debug.warn("Choice C"), // 70-99
}
switch (c) {
'a' upto 'c' => {}, // a, b
'c' uptoand 'e' => {}, // c, d, e
} |
Right now,
..
is the only slice operator available, and it is exclusive. Meanwhile...
(1 extra dot) is the only switch range operator available, and it is inclusive.I believe that the difference in exclusivity of each kind of expression is appropriate based on typical use cases, however the difference in syntax is subtle. It may be worth choosing more clear syntax to represent status quo, or perhaps adding the exclusive ability to switch statements.
Here's an example of a switch statement where exclusive ranges are better:
Right now this would give you an error because 30 and 70 are used twice. To fix it, the code would look like this:
It's not so bad, especially considering the
-1
happens at compile-time, but this is an example of where exclusive range is desired. Another example would be enum ranges. There is no reasonable way to do "enum value" minus 1. Another example would be if they were floats instead of integers. In this case -1 doesn't make sense and you absolutely need the exclusivity ability.Here are some proposals:
..
in switch as well as...
. This matches Perl - two dots is exclusive, three dots is inclusive...
slice syntax to:
. This matches Python. Switch statements still have no exclusive range operator...
slice syntax to:
, and allow:
in switch statements as well, so that they have an exclusive range operator available.If we have a for range syntax (See #358) then that should be taken into consideration as well.
The text was updated successfully, but these errors were encountered: