As a Chapel programmer, I'd like to see support added for "open interval" ranges that would omit the bound on one or both sides of the range. The proposal being that:
3..<6 == 3, 4, 5
3<..6 == 4, 5, 6
3<..<6 == 4,5
Note that when counting from 0, ..< is fairly similar to using # with an upper-unbounded range:
0..<n == 0..n-1 == 0..#n
but that for other low bounds it has a different / added benefit (e.g., "I want everything from k to the last element, except for the last element):
k..<hi == k..hi-1 != k..#hi
When implementing 0-based indexing, there were cases that this would've been helpful, and it received positive reviews on when discussed on issue #12988.
These could also be valuable if we were to support ranges of floating point values as has sometimes been suggested where the bound can't be dropped as simply as just adding or subtracting one.
This feature also has the benefit of increasing Chapel's emoji-compliance status! ;)
I like what the syntactic sugar provides, but am wondering if it is worth the added syntactic complexity to ranges, i.e. it is another range operator to learn / could reduce readability (hard to say)
One might also expect the symmetric syntax to work, even though it is far less useful: 1>..>10 == 0..11
@bradcray - I think you mentioned elsewhere that Swift supports something like this. Do you know of any other languages that do? Do they support the both > and <?
We would expect to fail if both </> and # were used together, e.g. 1..<#10, right?
@ben-albrecht: Swift is the only language I've seen this in, and I haven't seen any evidence that they support lower or both open intervals, only upper (but I haven't looked very hard either).
I don't think supporting > makes sense really. It's not mnemonic, and there's no mathematical correlation whereas closed (our default) and open intervals are well-defined in math. As a specific example, what would > mean on a range of reals?
@lydia-duncan: That specific example would fail (in my current thinking at least) because when you use an open interval, a specified bound is required syntactically. However, the # operator can be applied to any range (or domain or array), so the two could be mixed if you did something like this: 1..<10#5 which would mean 1..9#5 which would mean 1..5.
Searching a bit more, I'm finding this proposal which suggested adding <.. and <.< to round out ..< in Swift. But there aren't any indications that these gained traction.
I'd definitely agree that the lower- and both-open intervals are less likely to be used in practice than upper-open intervals, but think supporting all three rather than just one makes sense due to the symmetry with mathematics and within the operator family; I suspect that if/when we extend ranges to support floating point types, having all three will become more useful... e.g., I'm imagining expressions like if (-eps<..<eps).member(delta) or if delta in -eps<..<eps then....
The proposed syntax is intuitive for sure, and I can learn to live with it. I am not going so far as to say I would use it instead of doing the -1 or +1 "manually". I think my current thought is that it may be more prone to confusion than helpful.
Being able to do things like a<..<b would make me think like I could also do a<=..<=b which is the same thing as a..b and I don't want to read code that uses the former rather than the latter. Or similarly, maybe people might want to do upperLimit>..>lowerLimit because it is symmetric to lowerLimit<..<upperLimit mathematically speaking. But I can't tell whether upperLimit>..>lowerLimit has a stride of -1 or 1, nor it is something that I would like to think about while reading code.
I think I feel more comfortable with using this syntax when/if there is support for float-based ranges.
Important to note that I haven't used swift before.
@e-kayrakli: I agree that someone might want/wish for these things, but don't feel any need to provide them, personally.
To be clear, I don't think we should provide them either. But it makes me think whether we shouldn't even provide a<..<b, because they are arguably in the same league.
To be clear 2, I am not actively against this. But don't feel the need that much, and worry a tiny bit about this potential confusion.
@bryantlam: When this proposal came up on issue https://github.com/chapel-lang/chapel/issues/12988#issuecomment-495799198, you seemed pretty enthusiastic about it (or at least that was my read). Here, the response seems a bit more tepid, so I was not planning on adding it for this release (though it's implemented and easy to do so). Wanted to see if that seemed OK to you or if you wanted to advocate more strongly for it.
The most desired one is ..<. I could defer on the other ones until they're needed.
When you or whoever was porting the existing application codes that use bounded regions / halo exchanges, how idiomatic were their ranges without ..<? While this issue is not my biggest concern, I personally think it would be better to mitigate the (perceived) "error-prone-ness" of manually adjusting ranges with -1 when the range / range operator should handle it.
When you or whoever was porting the existing application codes that use bounded regions / halo exchanges, how idiomatic were their ranges without ..
In 95+% of the cases, I used 0..#n rather than 0..n-1 (where these could have equivalently been written 0..<n).
The other 5% where # wasn't applicable were cases like for i in foundPos..size-1 which is a case where for i in foundPos..<size would've been more attractive.
@ben-albrecht / @e-kayrakli: If this proposal were to simply support ..<, would that change your support from tepid to stronger?
1..<10#5which would mean1..9#5which would mean1..5
Something about 0..#n always bothered me but I couldn't figure out why until this statement was made. IMO, 0..<n is much more idiomatic of what a user would usually intend (bounded range from 0 to less than n) versus 0..#n (notionally an unbounded range from 0 to infinity, but is counted n times to bound it). The range 0..#n is trickier to understand what it is semantically doing on first appearance and is usually exceptional to use.
If this proposal were to simply support
..<, would that change your support from tepid to stronger?
I think the ..< form alone seems reasonable. I like that this keeps the possible range syntax variations relatively simple, which has advantages in learning curve and readability: m..n, m..#n, and m..<n (ignoring strides)
0..<nis much more idiomatic of what a user would usually intend (bounded range from 0 to less than n)
I agree 0..<n is pretty intuitive. I think it was the other variations that made me nervous about this proposal.
The other 5% where # wasn't applicable were cases like for i in foundPos..size-1 which is a case where for i in foundPos..
FWIW, This example warmed me up a bit to the syntax and made me appreciate its added value more. I am not against adding ..<
I find # intuitive or maybe got too used to it over time. So, in cases where # is still applicable, I think I'll still use that.
Don't interpret the following as advocating that we take all three operators for now, but before I forget:
It seems like lower-/both-open intervals could be useful in some domain situations like:
const D = {1..n, 1..n};
const Inner = {1<..<n, 1<..<n};
The following would take more work (since I'd previously only proposed supporting open intervals on ranges that have bounds), but you could also imagine using these to express interesting array slices like:
const lo = A.first;
for a in A[<..] do // iterate over all elements of A other than the first
lo = min(lo, a);
or
...A[<..<, <..<]... // get the inner elements of A
I've spun the leftover pieces of this relating to low- and both-open intervals to a new issue #15272 in order to close this one since we've added support for the upper-open intervals.
Most helpful comment
@ben-albrecht: Swift is the only language I've seen this in, and I haven't seen any evidence that they support lower or both open intervals, only upper (but I haven't looked very hard either).
I don't think supporting
>makes sense really. It's not mnemonic, and there's no mathematical correlation whereas closed (our default) and open intervals are well-defined in math. As a specific example, what would>mean on a range of reals?@lydia-duncan: That specific example would fail (in my current thinking at least) because when you use an open interval, a specified bound is required syntactically. However, the
#operator can be applied to any range (or domain or array), so the two could be mixed if you did something like this:1..<10#5which would mean1..9#5which would mean1..5.