CharRange not able to concat?


#1

Hi,

I see two overloaded plus() methods in CharRange, but then I can’t do simple thing as this:

('A' .. 'Z') + ('0' .. '9')

It fails to compile. I have call .toList() on both to concat them. Maybe we should improve this?

-Z


#2

What object are you expecting to get? Do you want to iterate over it or have quick lookup?

If you want to iterate, you can use iterator() or toList():

``

// this one is “lazy”, so it won’t require memory for all chars
for (i in (‘A’…‘Z’).iterator() + (‘0’…‘9’).iterator()) {
  // …
}

val list = (‘A’…‘Z’).toList() + (‘0’…‘9’).toList() // this one is useful if you need to pass list somewhere, or mutate list afterwards

For quick lookup this would require adding new class for this structure, and we don’t think it is necessary. Just concatenate iterators or lists, and then convert it to set.

Also, you can convert char range into string using makeString():

``

(‘0’…‘9’).makeString(separator = “”) // parameter name can be omitted, since it is the only one

Maybe this variant of makeString() deserves a shortcut in standard library, but I’m not sure yet: need to consider use cases.


#3

My point is why force user to explicitly call .iterator() or .toList() to concat two range type. One would expect CharRange + CharRange to return another CharRange. You guys do allow List + List to return another List, so why not CharRange, or any Range type for that matter.

I expect this to work, but it doesn’t:

for (ch in ('A'..'Z') + ('0'..'9')){   println(ch) }


#4

An object of type CharRange represents a range of characters, i.e. a set of characters which contains all and only those characters between its beginning and ending characters.

A sum of two objects of type CharRange is not always an object of type CharRange. E.g. in your example you can’t represent the result of the addition with a CharRange object, since it’s not a continuous set of characters.


#5

I suppose it might make sense to allow the plus and have the result be a Collection of characters:

val x: Collection<Char> = ('A' .. 'Z') + ('0'..'1')

But then again, I doubt for the relatively few use cases this is a whole lot better than just using toList().

#6

If we allow adding CharRanges, we should allow adding IntRanges, for symmetry. Then, let's consider the following code:

for (i in (0..1000000000) + (1500000000..2000000000)) { // from 0 to 1 million, from 1.5 million to 2 million   // ... }

It looks harmless, but it will consume from 6 or 12 GB (depending of 32-bit or 64-bit JVM).

We have the following agreement in standard library now: plus (and other similar operations) are defined for Iterator and for Collection, and they are implemented differently. For Iterator it is lazy: doesn’t process all elements at once, but creates iterator which will work like an iterator over merged collections. For Collection it is strict (creates new collection and copies all elements there). We don’t have such functions for Iterable (and CharRange is Iterable), because it’s unclear, what semantic does developer expect: Iterable may mean just a flow of objects from database or something like that. Developer needs to get Iterator from it or convert to collection (this may require elements copying), to make it clear what semantic they expect.

The same applies to ranges: if you want to iterate, without consuming extra memory for storing all elements: get iterators and concatenate them. If you want to perform contains-check, convert it ranges to sets.