We would like to start a discussion around enchanc...
# language-proposals
g
We would like to start a discussion around enchanced ranges/slices and hopefully end up with a KEEP. Roughly the same set of questions is being regularly asked by people that come from a different language background: 1. Support for half-open range 2. Support for partial range (from, to, through) 3. Array/List slices. Possibly backed by before-mentioned ranges syntax. (all of this seem to be possible to implement without breaking changes in Kotlin) Currently, we propose to focus on the first one, in particular: adding
<
operator for
until
function. Gradually, having half-open and partial ranges, the slices syntax should follow. Given: Already implemented "closed range" in Kotlin. Since Kotlin range is closed by default, by likeness, Groovy and Swift might be considered as an inspiration: Closed range: Swift:
0...5
Groovy:
0..5
Half-open range: Swift:
0..<5
,
...<5
// compare to
val a = until 5 ???
Groovy:
0..<5
Slice: Swift:
arr[1...5], arr[1..<5]
Groovy:
arr[1..5]
====================================== Why it's important, why not
until
, why partial range?
Personally, I believe that the closer we can get to the formal requirement (in this case — mathematical interval) the better. Thus, you don't execute the code but the requirement itself (it is only because of imperfection of our reality we're forced to write code around formal requirements 😉). That's why
+
is better than
plus()
. The former is essential and the latter is induced workaround. Also imho, with KISS in mind, there is no need to reinvent slices syntax (I've seen
:
proposal) when it's already covered by good range system. But it's a different discussion and I might be missing a bigger picture. Does
(1..<5)
look reasonable to you?
cc: @Dico, @Mike, @karelpeeters, @Hullaballoonatic
👍 13
t
I think @thomasnield has done some work on this and has started a KEEP. Not sure about the overlap (I’m on mobile and can’t focus right now). https://github.com/thomasnield/KEEP/blob/3a72cf68902fafdfd7aabfce3e668ce294119d3b/proposals/stdlib/exclusive-ranges.md
d
The half open range looks reasonable. When reading the code below, it’s behaviour is directly clear to me:
Copy code
val x = 0..3
    val y = 0..<4
    val z = 0 until 4
k
Might want to consider
0<..4
and
0<..<4
too for the other half open and open ranges.
(this is more relevant for doubles and in general comparables than for ints)
d
Hmm, @karelpeeters earlier I thought it should be
0>..<4
but it makes more sense to have the arrows the same way 😅
So what we're expressing is
0 < x < 4
k
Yeah exactly.
d
Very well written post @ghedeon
🙏 1
d
By far my most common use case for partial ranges is for loops, and I'm not convinced that being able to write
for(i in 0..<5) { ... }
instead of
for(i in 0 until 5) { ... }
is worth adding another operator to the language. Are there any syntactical cases other people run in to where
..<
disambiguates or simplifies an expression by maybe removing parentheses, for example? I'm not sure this is comparable to common mathematical operators like
+
, because if you're working with something like a BigInteger the readability gains are obvious.
use cases date/time ranges and pagination - esp. the former is often half-open
g
nice. Wasn't aware and it's fresh, just yesterday. It's popping up too often to ignore it.
👍 2
I'd need some cooperation from more experienced contributors. Is KEEP with some draft implementation is the next logical step? The one linked above doesn't have the implementation, doesn't mention the operator and is heavily inclined towards Double & Float support. Shall I create a separate one for half-open ranges for integral types? Should we have a separate KEEP for partial ranges?
v
Could you please elaborate on the use cases for open ranges, how often they happen, what are the workarounds, why a special syntax is significantly better. Without the use cases, one could argue that the whole discussion about the implementation details is meaningless.
2
m
@dalexander You can also write
repeat(5) {}
and use
it
in the block, although I didn’t realize repeat passed in the index until this thread.
k
I've known about it but I feel like it misrepresents intent:
repeat
insinuates that the code inside it does about the same thing every time and is not dependent on the index.
g
@voddan Correct, good use-cases is what this thread for. I feel like special syntax is a superset to
until
. And it scales further to slices and partial ranges. Not sure how one would express a partial range like
..<n
with
until
. Now, if I got it right, we have a case where
until
is not enough for date and time periods (see @Marc Knaup's link). Plus, it's shorter and self-explanatory, probably the reason it's used in other languages. So yeah, feel free to throw at me all the special cases where you feel like
until
is unnatural or hard to reason about.
m
For me
until
is just a hack to partially support half-open range for types whose values have a natural predecessor or successor (e.g. 0 <-> 1 <-> 2) - by turning them into a normal
Range
using `to`'s predecessor. That means it won't work for example for Float, Double, String, dates, times and other potentially complex
Comparable
types which don't have a clear predecessor. E.g. what's the predecessor of 12pm? 11:59? 115959? 115959.999? 115959.999999999? … Or for
"abc"
?
"abb"
?
"abbz…"
? …
until
also doesn't work for minimum
to
values.
999 until Int.MIN_VALUE
would result in an artificial range
1 to 0
which can be totally unexpected. While it doesn't make sense to use
MIN_VALUE
like in this example, in a dynamic scenario
to
could be passed
MIN_VALUE
which should result in a proper empty range with the
start
unaltered.
v
until
was specifically made for using in loops `for(i in 0 until length)`because that was the most common use case for open ranges in other languages. It was never supposed to substitute true open ranges because there wasn't any evidence that open ranges are useful outside of some narrow niches.
1
What I see in this thread is a discussion on
how
to do open ranges. But
why
is far more important.
3
I have an extended experience with Python which has open ranges, and I work with mathematical code from time to time, but so far I've never missed open ranges in Kotlin
On the contrarily, I am very glad that Kotlin only has closed ranges, so when people need exclusive bounds, they have to use a much more readable
a < x < b
Please do't take it the wrong way, I am all for the innovation in the language. But in a KEEP for open ranges you'll have to push against 2 types of people. First, language designers who don't want to support a niche feature for the benefit of a small group. And second, people like me who enjoy the lack of an easy syntax for open ranges because we had extremely bad experiences with other languages which supported open range operators like
a..<b
. In both of those discussions your "why" is extremely important and has to be proven case-by-case. "Nice to have" or "others have it" is usually not good enough.
💯 2
m
My back-end is full of usages of my own
HalfOpenRange
but almost always for temporal ranges. I'm fine with it being an stdlib feature instead of a language feature with own syntax. Just some kind of standard would be great. I agree that it doesn't necessarily need an update to the language itself. I don't have any experience in using partial ranges so I can't say much about these.
g
@voddan Do you mind to elaborate on "extremely bad experience with
a..<b
." Like, I understand that it's an added functionality that not everyone is interested in, but how one can get an extremely bad experience from
a..<b
and not from
a until b
?
c
Some domains are more naturally expressed as half-open range, for example validity period of some offering or filter presets like all transactions lower than 100$
d
I think there should be an equivalent to
..
for half open ranges because they are simply way more common than the closed range you get with
..
. I'm reminded everytime I write a for loop over a range.
I like the extensibility of the idea into python like slices.
v
@ghedeon Maybe it is just me, but ranges/slices in Python always add me a cognitive load because I need to calculate the upper bound in my head to verify that the corner cases of my program work as expected. Simply put, IMHO open ranges are easier to write, but are far harder to read and debug. Adding a nice syntax for them would increase their appearance in code
d
@voddan I do not have such experiences
v
@Dico glad to hear that 🙂 Everybody is different, every feature has its pros and cons. That's why I am interested to hear when and where people realy need open ranges and using other stuff was ugly and painful 😛
d
Indeed. Language design is difficult because everyone is different. Statistics of kotlin developers would be cool to have on issues like this.
m
Here’s a perfect example. Kotlin designers did data class, but here’s what the Java designers are going through for the same thing. Many viewpoints and definitions. Really highlights how hard language design is, but also explains why there are so many of them… https://cr.openjdk.java.net/~briangoetz/amber/datum.html