www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - First class lazy Interval

reply bearophile <bearophileHUGS lycos.com> writes:
D2 supports the interval syntax in the foreach:
foreach (i; 1..1000) {...}

Such intervals are useful in a very large number of situations. So, with the
new Range support, it may be useful to allow the interval syntax to be used in
other contexts as well.
So x..y may become a first-class lazy interval from x to y-1, that can be
passed to functions too, etc, and not just used into foreach (the compiler can
recognize it, and often optimize it away in many situations, replacing it with
a normal for() loop).

Optional possibilities:

1) x..y..s
where s is a step/stride, so you can define odd numbers, etc.
1..10..3 ==> 1 4 7
10..1..-2 ==> 10 8 6 4 2
1..5..1 ==> 1 2 3 4
I don't like this syntax much, but I think it's acceptable.

2) Such intervals may enjoy a fast opIn_r() method, for quick membership test:
if (a in 0..10) {...}
if (c in 'c'..'z'+1) {...} 
(Notice the +1 because all intervals are open on the right).
For the compiler that's syntax sugar of (a>0 && a<10).
For the user I think it's easy to remember such syntax.

Originally I have thought about the syntaxes [x..y] and [x..y..s] as eager,
that is arrays of the (integer?) numbers of the interval. But they are just
sugar for the (future?) function of std.algorithm that produces an array from a
lazy iterable, so it may not be useful enough.

Bye,
bearophile
Feb 27 2009
next sibling parent bearophile <bearophileHUGS lycos.com> writes:
bearophile:
 For the compiler that's syntax sugar of (a>0 && a<10).
I meant: (a >= 0 && a < 10)
Feb 27 2009
prev sibling next sibling parent reply Michel Fortin <michel.fortin michelf.com> writes:
On 2009-02-27 04:43:46 -0500, bearophile <bearophileHUGS lycos.com> said:

 D2 supports the interval syntax in the foreach:
 foreach (i; 1..1000) {...}
 
 Such intervals are useful in a very large number of situations. So, 
 with the new Range support, it may be useful to allow the interval 
 syntax to be used in other contexts as well.
 So x..y may become a first-class lazy interval from x to y-1, that can 
 be passed to functions too, etc, and not just used into foreach (the 
 compiler can recognize it, and often optimize it away in many 
 situations, replacing it with a normal for() loop).
I agree that having first-class intervals in the language would make it better, especially when you want to pass intervals as function arguments. In the D/Objective-C bridge, I've defined the NSRange struct (a Cocoa type representing an integer interval) so it can be created by typing NSRange[start..end] in adition to the traditional NSMakeRange(start, length). It's better than nothing, but even better would be the ability to omit NSRange[] entirely.
 1) x..y..s
 where s is a step/stride, so you can define odd numbers, etc.
 1..10..3 ==> 1 4 7
 10..1..-2 ==> 10 8 6 4 2
 1..5..1 ==> 1 2 3 4
 I don't like this syntax much, but I think it's acceptable.
May I propose that strides not be part of the interval syntax. Keep the interval simple (x..y) then define operators to transform them. Perhaps the modulus operator could create a steped interval (x..y % s), although this operator doesn't seem to fit exactly right. Perhaps just step(x..y, s) would be enough. It'd return a special struct for iterating the interval in bigger steps. As for strides, why not just (a..b ~ c..d) for joining two ranges and ~(x..y) for everything outside x..y ?
 2) Such intervals may enjoy a fast opIn_r() method, for quick membership test:
 if (a in 0..10) {...}
 if (c in 'c'..'z'+1) {...}
That'd be great if you could use them in the switch statement too: switch (a) { case 0..10: break; } The only downside is that it may confuse people who are accustomed to the GCC extension with define an inclusive interval this way: switch (a) { case 0...10: break; } Perhaps we could make 3 dots mean an inclusive interval (including the second value in the interval), and 2 dots an exclusive one (excluding the second value). -- Michel Fortin michel.fortin michelf.com http://michelf.com/
Feb 27 2009
next sibling parent reply bearophile <bearophileHUGS lycos.com> writes:
Michel Fortin:
 In the D/Objective-C bridge, I've defined the NSRange struct (a Cocoa 
 type representing an integer interval) so it can be created by typing 
 NSRange[start..end] in adition to the traditional NSMakeRange(start, 
 length). It's better than nothing, but even better would be the ability 
 to omit NSRange[] entirely.
In my dlibs I have xrange/range, but I am also looking for something that the compiler can recognize and optimize away better.
 As for strides, why not just (a..b ~ c..d) for joining two ranges and 
 ~(x..y) for everything outside x..y ?
Weeks ago I have strongly suggested Alex to add support for ~ to join any kind of lazy/eager iterable to any other lazy/eager iterable (if the types of the items they yield are equal or castable). I have defined xchain and the Chainable mixin in dlibs for this very useful purpose.
 The only downside is that it may confuse people who are accustomed to 
 the GCC extension with define an inclusive interval this way:
Too bad for them.
 Perhaps we could make 3 dots mean an inclusive interval (including the 
 second value in the interval), and 2 dots an exclusive one (excluding 
 the second value).
Ruby follows your idea, but for the eye it's easy to miss the extra dot, so I don't like this much (but I think an optional stride syntax may be useful, especially if such stride can be negative too. You can also perform a little optimization if the stride is known at compile time, as it often happens). Bye, bearophile
Feb 27 2009
parent Lutger <lutger.blijdestijn gmail.com> writes:
bearophile wrote:

 Michel Fortin:
...
 Perhaps we could make 3 dots mean an inclusive interval (including the 
 second value in the interval), and 2 dots an exclusive one (excluding 
 the second value).
Ruby follows your idea, but for the eye it's easy to miss the extra dot,
so I don't like this much (but I think an optional stride syntax may be useful, especially if such stride can be negative too. You can also perform a little optimization if the stride is known at compile time, as it often happens).
 
 Bye,
 bearophile
Except in Ruby the meaning is reversed (.. is inclusive and ... is exclusive). About the stride, in ruby this is done as (0..10).step(2), which is very readable imho. With extension methods if these ever get in D, will the same syntax be possible with std.range? I do like the stride in std.range (and step in ruby) because it's straightforward and readable: stride( a, 2 ) could become: stride( 0..10, 2 ) or: (0..10).stride(2)
Feb 27 2009
prev sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Michel Fortin wrote:
 On 2009-02-27 04:43:46 -0500, bearophile <bearophileHUGS lycos.com> said:
 
 D2 supports the interval syntax in the foreach:
 foreach (i; 1..1000) {...}

 Such intervals are useful in a very large number of situations. So, 
 with the new Range support, it may be useful to allow the interval 
 syntax to be used in other contexts as well.
 So x..y may become a first-class lazy interval from x to y-1, that can 
 be passed to functions too, etc, and not just used into foreach (the 
 compiler can recognize it, and often optimize it away in many 
 situations, replacing it with a normal for() loop).
I agree that having first-class intervals in the language would make it better, especially when you want to pass intervals as function arguments.
I'm having trouble understanding what's wrong with the good old data types and functions. Andrei
Feb 27 2009
next sibling parent reply "Denis Koroskin" <2korden gmail.com> writes:
On Fri, 27 Feb 2009 16:44:31 +0300, Andrei Alexandrescu
<SeeWebsiteForEmail erdani.org> wrote:

 Michel Fortin wrote:
 On 2009-02-27 04:43:46 -0500, bearophile <bearophileHUGS lycos.com>  
 said:

 D2 supports the interval syntax in the foreach:
 foreach (i; 1..1000) {...}

 Such intervals are useful in a very large number of situations. So,  
 with the new Range support, it may be useful to allow the interval  
 syntax to be used in other contexts as well.
 So x..y may become a first-class lazy interval from x to y-1, that can  
 be passed to functions too, etc, and not just used into foreach (the  
 compiler can recognize it, and often optimize it away in many  
 situations, replacing it with a normal for() loop).
I agree that having first-class intervals in the language would make it better, especially when you want to pass intervals as function arguments.
I'm having trouble understanding what's wrong with the good old data types and functions. Andrei
The syntax. One may want to reuse 0..100 syntax to generate random number: auto x = random(0..100); // gimme a random value in [0, 100) or check if a value belongs to an interval: T opIndex(size_t index) { assert(index in 0.._size); // ... }
Feb 27 2009
next sibling parent Don <nospam nospam.com> writes:
Denis Koroskin wrote:
 On Fri, 27 Feb 2009 16:44:31 +0300, Andrei Alexandrescu 
 <SeeWebsiteForEmail erdani.org> wrote:
 
 Michel Fortin wrote:
 On 2009-02-27 04:43:46 -0500, bearophile <bearophileHUGS lycos.com> 
 said:

 D2 supports the interval syntax in the foreach:
 foreach (i; 1..1000) {...}

 Such intervals are useful in a very large number of situations. So, 
 with the new Range support, it may be useful to allow the interval 
 syntax to be used in other contexts as well.
 So x..y may become a first-class lazy interval from x to y-1, that 
 can be passed to functions too, etc, and not just used into foreach 
 (the compiler can recognize it, and often optimize it away in many 
 situations, replacing it with a normal for() loop).
I agree that having first-class intervals in the language would make it better, especially when you want to pass intervals as function arguments.
I'm having trouble understanding what's wrong with the good old data types and functions. Andrei
The syntax. One may want to reuse 0..100 syntax to generate random number: auto x = random(0..100); // gimme a random value in [0, 100) or check if a value belongs to an interval: T opIndex(size_t index) { assert(index in 0.._size); // ... }
Particularly, multi-dimensional slices. We have opSlice as a hacky special case of opIndex. So desirable syntax like auto y = x[2..$, 4, 3..8]; is impossible. You can only do something like: auto y = x[Range(2,$), 4, Range(3,8)]; or perhaps auto y = x[Range[2..$], 4, Range[3..8]];
Feb 27 2009
prev sibling parent reply Daniel Keep <daniel.keep.lists gmail.com> writes:
Denis Koroskin wrote:
 On Fri, 27 Feb 2009 16:44:31 +0300, Andrei Alexandrescu
 <SeeWebsiteForEmail erdani.org> wrote:
 I'm having trouble understanding what's wrong with the good old data
 types and functions.

 Andrei
The syntax. One may want to reuse 0..100 syntax to generate random number: auto x = random(0..100); // gimme a random value in [0, 100) or check if a value belongs to an interval: T opIndex(size_t index) { assert(index in 0.._size); // ... }
I don't think this should be in the language... it just feels like there isn't enough use for it. Especially not when you can implement it as a library. As proof, I've attached an implementation of an integral interval. Here's a sample of usage (the program prints this if you run it):
 inter[0, 10] = inter[0, 10]
 inter[0..10] = inter[0, 9]
 inter(0, 10) = inter[1, 9]
[a,b] is inclusive, (a,b) is exclusive and [a..b] is lower inclusive, upper exclusive (to match slicing syntax).
 inter[0..10] % 2 = inter[0, 8] / 2
 inter[1..10] % 2 = inter[2, 8] / 2
 inter[1..10] / 2 = inter[1, 9] / 2
 6 in inter[0..10] % 2 = true
 7 in inter[0..10] % 2 = false
I % a produces an interval of all values n in I which satisfy: n % a == 0. I / a produces an interval containing every a'th element of I (including the first).
 inter[0, 30] / 2 = inter[0, 30] / 2
 (inter[0, 30] / 2).length = 16
 (inter[0, 30] / 2) / 3 = inter[0, 30] / 6
 ((inter[0, 30] / 2) / 3).length = 6
 ((inter[0, 30] / 2) / 3).toArray = [0 6 12 18 24 30]
It should support indexing, slicing and length-ing. It should also support ranges (can't test it, of course :P). -- Daniel
Feb 27 2009
next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Daniel Keep wrote:
 I don't think this should be in the language... it just feels like there
 isn't enough use for it.  Especially not when you can implement it as a
 library.
 
 As proof, I've attached an implementation of an integral interval.
 Here's a sample of usage (the program prints this if you run it):
This is pretty darn cool! Andrei
Feb 27 2009
parent reply Daniel Keep <daniel.keep.lists gmail.com> writes:
Andrei Alexandrescu wrote:
 Daniel Keep wrote:
 I don't think this should be in the language... it just feels like there
 isn't enough use for it.  Especially not when you can implement it as a
 library.

 As proof, I've attached an implementation of an integral interval.
 Here's a sample of usage (the program prints this if you run it):
This is pretty darn cool! Andrei
Thanks. On the off chance anyone wants to actually use it, I hereby release the interval module attached to my previous post into the public domain. Anyway, it's 2:30 am here, so I'm off to bed. :P -- Daniel
Feb 27 2009
next sibling parent reply bearophile <bearophileHUGS lycos.com> writes:
Don:

How do you specify a uint range that includes uint.max?<
The interval I was talking about may generate integers only (it may be possible to generate longs and chars too if you give it long/char extrema, but I am not sure this is a good idea).
This is a perfect example of what Andrei recently posted: proposal of a
language change for a pathetically limited special case. Your stride is not
powerful enough to be worthwhile.<
It's limited on purpose, but such intervals are useful in many situations. And note such interval is less limited than the current syntax of D2, that allows such interval as foreach argument only. This post of mine was just an idea, surely most of it can be improved :-) --------------------- Daniel Keep:
[a,b] is inclusive, (a,b) is exclusive and [a..b] is lower inclusive, upper
exclusive (to match slicing syntax).<
I'll probably not be able to remember that. What I'm looking for is something simple that covers the most common cases, not all of them introducing excessive burden to the memory. In designing such things the 80/20 rule is good. --------------------- Don:
 So desirable syntax like auto y = x[2..$, 4, 3..8]; is impossible.<
I see. In Haskell you can use the syntax [x..] to denote the half interval of integers from x to infinite. So in D2 x..$ may be used to denote an interval infinte on the right... or (contextual-wise) one interval extended to the max possible value (max int, or max length of the iterable/data structure?). I am not sure such double meaning is a good thing. --------------------- Lutger:
About the stride, in ruby this is done as (0..10).step(2), which is very
readable imho.<
It's more readable than 0 .. 10 .. 2 but I think you can get used to 0..10..2 too, because you probably end using such syntax often in programs. --------------------- Sean Reque:
but Ruby also supports instantiating them using perl's syntax, which underneath
the hood is just creating a Regexp object. The advantage of the perl-like
syntax is that editors and IDEs can aid a user with syntax highlighting,
whereas when creating a regular expression with a string no such help can be
given.<
Languages are designed for different purposes and with different ideas in mind. Syntax-wise Python is simpler, less tricky (*) and more uniform than Ruby. This makes Python less complex to learn, but less able to create custom-purpose sub-languages, etc. (* = see functions called as foo 5)
Doing things similarly in D, one could implement intervals in a library form in
a similar manner as Daniel did, and then add support in the language for the
syntax bearophile described.<
Few weeks ago I have suggested to adopt a similar strategy in D too, for example to implement complex numbers, multi-precision numbers, and few other things. The compiler can implement just the syntax and a default "bridge" from such syntax and the logic that is contained into a normal D module of the std lib. So for example you can write: Bigint a = 123_456_789_123_456_789_123_456_789; Bigint b = 987_654_321_987_654_321_987_654_321; c = a * b; Where the compiler has a built-in support such natural syntax for Bigints literals (or complex number literals), but the operations like the multiplication among such Bigints are implemented in D into the std.bigints module and not into the compiler. I don't actually know if such idea can be implemented in a D compiler, no one has answered me about this so far :-) --------------------- A possible small problem: Floating-point linear ranges (as used in matlab and numPy, etc) are often useful, but the 0..10 and 0..10..2 syntax doesn't look much good if you want to re-use it for floats (or even for interval arithmetic) because all those points may be too much confusing, even if you add spaces: 0.0 .. 10.5 .. 0.5 0. .. 10. .. .5 So intervals may be kept for integral values only. As an alternative syntax may be: foreach (i; 1:10) {...} foreach (i; 10 : 1 : -1) {...} foreach (i; 0.0 : 10.5 : 0.5) {...} Bye, bearophile
Feb 27 2009
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
bearophile wrote:
 As an alternative syntax may be:
 
 foreach (i; 1:10) {...}
 foreach (i; 10 : 1 : -1) {...}
 foreach (i; 0.0 : 10.5 : 0.5) {...}
I have an idea - we define a contextual keyword "iota" that helps us specify from, to, and stride. Then we can write: foreach (i; iota(0.0, 10.5, 0.5)) {...} Andrei
Feb 27 2009
parent downs <default_357-line yahoo.de> writes:
Andrei Alexandrescu wrote:
 bearophile wrote:
 As an alternative syntax may be:

 foreach (i; 1:10) {...}
 foreach (i; 10 : 1 : -1) {...}
 foreach (i; 0.0 : 10.5 : 0.5) {...}
I have an idea - we define a contextual keyword "iota" that helps us specify from, to, and stride. Then we can write: foreach (i; iota(0.0, 10.5, 0.5)) {...} Andrei
How about the more intuitive "foreach (i; 0 .. 10.5 stride 0.5)"? It doesn't always have to be a symbol. ^^
Feb 28 2009
prev sibling parent Jesse Phillips <jessekphillips gmail.com> writes:
On Sat, 28 Feb 2009 02:33:15 +1100, Daniel Keep wrote:

 Andrei Alexandrescu wrote:
 Daniel Keep wrote:
 I don't think this should be in the language... it just feels like
 there isn't enough use for it.  Especially not when you can implement
 it as a library.

 As proof, I've attached an implementation of an integral interval.
 Here's a sample of usage (the program prints this if you run it):
This is pretty darn cool! Andrei
Thanks. On the off chance anyone wants to actually use it, I hereby release the interval module attached to my previous post into the public domain. Anyway, it's 2:30 am here, so I'm off to bed. :P -- Daniel
Are you even allowed to release code to public domain that late at night? :D
Feb 27 2009
prev sibling parent reply Michel Fortin <michel.fortin michelf.com> writes:
On 2009-02-27 09:47:30 -0500, Daniel Keep <daniel.keep.lists gmail.com> said:

 assert(index in 0.._size);
I don't think this should be in the language... it just feels like there isn't enough use for it. Especially not when you can implement it as a library.
Well, if a..b simply maps to an interval type in the standard library (as I think it should) then you can implement the "in" operator in the standard library, outside of the language, if you want. -- Michel Fortin michel.fortin michelf.com http://michelf.com/
Feb 27 2009
parent reply Daniel Keep <daniel.keep.lists gmail.com> writes:
Michel Fortin wrote:
 On 2009-02-27 09:47:30 -0500, Daniel Keep <daniel.keep.lists gmail.com>
 said:
 
 assert(index in 0.._size);
I don't think this should be in the language... it just feels like there isn't enough use for it. Especially not when you can implement it as a library.
Well, if a..b simply maps to an interval type in the standard library (as I think it should) then you can implement the "in" operator in the standard library, outside of the language, if you want.
I think a lot of my uneasiness with having intervals in the language is the syntax. Yes, a..b is very nice. It's also a bad syntax for intervals. As Don keeps pointing out, you can't have an interval that includes int.max with that syntax. Also, if you say to someone, "we have intervals, look: [a..b]!" I'd be surprised if a good proportion of people didn't assume that it was inclusive both sides. That's how it'd be interpreted by math people. Using ... to indicate an inclusive range doesn't solve the problem. How do you do (a,b]? Introduce yet another operator? What about (a, b)? The only syntax I've ever liked for ranges is the math syntax. Sadly, it's pretty horrible for a programming language, and I don't think it's even context free. a..b in slicing and foreach is OK because it's for a very specific, well-understood purpose. It's like how range(5) in Python generates the sequence [0, 1, 2, 3, 4] because 90% of the time, that's what you want in the circumstances where it's commonly used. The argument that you can now easily test to see if a number is in a range is also a little underwhelming. It's not THAT hard to write the test out manually. On the whole, I think the use cases for this sort of syntax are just far too few and specialised to warrant syntax for them, especially when the syntax proposed is very restrictive. Especially when you can do it in a library without too much effort. Hell, my first instinct was to alias inter to Z to make it even shorter. Then it's only 3 characters more than native syntax, and more flexible. I understand the desire to see something potentially useful in the language. I just don't want to see D become like Perl (or to an extent, C++) where every feature that's even a little useful gets added, and we end up with a language where everyone has to learn and program in "subsets" of it, because no normal person could possibly remember how the whole thing works. -- Daniel
Feb 27 2009
next sibling parent Michel Fortin <michel.fortin michelf.com> writes:
On 2009-02-27 23:02:30 -0500, Daniel Keep <daniel.keep.lists gmail.com> said:

 On the whole, I think the use cases for this sort of syntax are just far
 too few and specialised to warrant syntax for them, especially when the
 syntax proposed is very restrictive.
 
 Especially when you can do it in a library without too much effort.
 Hell, my first instinct was to alias inter to Z to make it even shorter.
  Then it's only 3 characters more than native syntax, and more flexible.
But what I'm proposing is that a..b just become a shortcut for inter[a..b], or whatever is the standard interval struct in the standard library. Isn't that exactly what you're trying to do (a shortcut) by shortening inter[a..b] to Z[a..b] ?
 Using ... to indicate an inclusive range doesn't solve the problem.  How
 do you do (a,b]?  Introduce yet another operator?  What about (a, b)?
Perhaps using "..." was a bit too much to ask. But there is no reason why the standard library interval cannot support all kinds of bounds just like inter does. The syntax just won't be as nice as a..b. Basically, all the compiler would do is map a..b to inter[a..b] and leave everything else to the standard library. So it's not removing any option, it just consistently allow you to write a..b whatever the place. -- Michel Fortin michel.fortin michelf.com http://michelf.com/
Feb 27 2009
prev sibling next sibling parent reply Bill Baxter <wbaxter gmail.com> writes:
On Sat, Feb 28, 2009 at 1:02 PM, Daniel Keep <daniel.keep.lists gmail.com>wrote:

 Yes, a..b is very nice.  It's also a bad syntax for intervals.  As Don
 keeps pointing out, you can't have an interval that includes int.max
 with that syntax.
4..$ 4u..$ etc? --bb
Feb 27 2009
parent Rainer Deyke <rainerd eldwood.com> writes:
Bill Baxter wrote:
 4..$
 4u..$
 etc?
Aside from the inconsistent meaning of $, you still can't have an inclusive range [a, b], where 'a' and 'b' are not only known at runtime, without treating 'b == int.max' as a special case. -- Rainer Deyke - rainerd eldwood.com
Feb 27 2009
prev sibling parent reply "Joel C. Salomon" <joelcsalomon gmail.com> writes:
Daniel Keep wrote:
 Yes, a..b is very nice.  It's also a bad syntax for intervals.  As Don
 keeps pointing out, you can't have an interval that includes int.max
 with that syntax.
4 .. int.$ —Joel Salomon
Mar 02 2009
parent reply Don <nospam nospam.com> writes:
Joel C. Salomon wrote:
 Daniel Keep wrote:
 Yes, a..b is very nice.  It's also a bad syntax for intervals.  As Don
 keeps pointing out, you can't have an interval that includes int.max
 with that syntax.
4 .. int.$ —Joel Salomon
I love that real.$ is the number that's even bigger than infinity <g>. What if the range is stored in variables? x[a..b]; How can you set b so that it includes x[int.max]? BTW, I'm not merely being negative, I'd love for one of these schemes to work.
Mar 03 2009
parent reply Tomas Lindquist Olsen <tomas.l.olsen gmail.com> writes:
On Tue, Mar 3, 2009 at 5:22 PM, Don <nospam nospam.com> wrote:
 Joel C. Salomon wrote:
 Daniel Keep wrote:
 Yes, a..b is very nice. =C2=A0It's also a bad syntax for intervals. =C2=
=A0As Don
 keeps pointing out, you can't have an interval that includes int.max
 with that syntax.
4 .. int.$ =E2=80=94Joel Salomon
I love that real.$ is the number that's even bigger than infinity <g>. What if the range is stored in variables? x[a..b]; How can you set b so that it includes x[int.max]?
How about x[a..b] vs x[a..b+] ?
Mar 03 2009
parent "Denis Koroskin" <2korden gmail.com> writes:
On Tue, 03 Mar 2009 19:37:13 +0300, Tomas Lindquist Olsen
<tomas.l.olsen gmail.com> wrote:

 On Tue, Mar 3, 2009 at 5:22 PM, Don <nospam nospam.com> wrote:
 Joel C. Salomon wrote:
 Daniel Keep wrote:
 Yes, a..b is very nice.  It's also a bad syntax for intervals.  As Don
 keeps pointing out, you can't have an interval that includes int.max
 with that syntax.
4 .. int.$ —Joel Salomon
I love that real.$ is the number that's even bigger than infinity <g>. What if the range is stored in variables? x[a..b]; How can you set b so that it includes x[int.max]?
How about x[a..b] vs x[a..b+] ?
What would the signature of opSlice be in both case? Or different methods invoked?
Mar 03 2009
prev sibling parent reply Michel Fortin <michel.fortin michelf.com> writes:
On 2009-02-27 08:44:31 -0500, Andrei Alexandrescu 
<SeeWebsiteForEmail erdani.org> said:
 Michel Fortin wrote:
 On 2009-02-27 04:43:46 -0500, bearophile <bearophileHUGS lycos.com> said:
 
 D2 supports the interval syntax in the foreach:
 foreach (i; 1..1000) {...}
 
 Such intervals are useful in a very large number of situations. So, 
 with the new Range support, it may be useful to allow the interval 
 syntax to be used in other contexts as well.
 So x..y may become a first-class lazy interval from x to y-1, that can 
 be passed to functions too, etc, and not just used into foreach (the 
 compiler can recognize it, and often optimize it away in many 
 situations, replacing it with a normal for() loop).
I agree that having first-class intervals in the language would make it better, especially when you want to pass intervals as function arguments.
I'm having trouble understanding what's wrong with the good old data types and functions.
Nothing, really. Specifying intervals as two separate function arguments as in f(a, b) is perfectly acceptable. Having a struct Interval { int a, b } is also a nice way to define and pass an interval arround. And defining intervals as a..b in foreach and when slicing array is a pretty neat syntax. But while all these solutions for passing intervals are nice, having an incoherent mix of all these isn't. I think intervals should be uniformized, and that probably goes by openning the a..b syntax to other uses. That could be done easily by mapping a..b to a struct in the standard library. That said, as others have pointed out, it also opens some new possibilities: It makes it easier to support multidimentional arrays. For instance, marray[1..3, 3, 3..4] could translate to a call to marray.opSliceIndex(interval!(int), int, interval!(int)). Making intervals a type generalizes them as ranges. For instance you could create a "random" template function that chooses a random element in a random-access range: random(range), then use that same function with an integer interval: random(1..10). That's an improvement over having two arguments: random(1, 10), because a two-arguments tuple can't be treated as a range (with front, back, popFront, popBack, etc.). And while having random(interval(1, 10)) is technically the same, it also is a lot more cluttered. If the language can avoid the clutter in foreach and array slices, then why can't we avoid it elsewhere? Why does an interval when inside a language construct looks better than elsewhere? That's the oddities mapping a..b to a standard type usable everywhere would avoid. -- Michel Fortin michel.fortin michelf.com http://michelf.com/
Feb 27 2009
parent Lutger <lutger.blijdestijn gmail.com> writes:
Michel Fortin wrote:
...
 If the language can avoid the clutter in foreach and array slices, then 
 why can't we avoid it elsewhere? Why does an interval when inside a 
 language construct looks better than elsewhere? That's the oddities 
 mapping a..b to a standard type usable everywhere would avoid.
It would be more consistent and nice if a..b would work everywhere without problems, but imho inter(a,b) is not that much clutter. Especially when strides come into play, I think stride(range, x) is much more readable than adding even more operators. What would really make ranges more readable is extension methods to get rid of all the nesting. After two of three levels of nesting it's starts to get quite annoying. Reading from left to right is just more natural than from the inside out.
Feb 28 2009
prev sibling next sibling parent Don <nospam nospam.com> writes:
bearophile wrote:
 D2 supports the interval syntax in the foreach:
 foreach (i; 1..1000) {...}
 
 Such intervals are useful in a very large number of situations. So, with the
new Range support, it may be useful to allow the interval syntax to be used in
other contexts as well.
 So x..y may become a first-class lazy interval from x to y-1, that can be
passed to functions too, etc, and not just used into foreach (the compiler can
recognize it, and often optimize it away in many situations, replacing it with
a normal for() loop).
How do you specify a uint range that includes uint.max? (This is a general problem with "[)" ranges).
 Optional possibilities:
 
 1) x..y..s
 where s is a step/stride, so you can define odd numbers, etc.
 1..10..3 ==> 1 4 7
 10..1..-2 ==> 10 8 6 4 2
 1..5..1 ==> 1 2 3 4
 I don't like this syntax much, but I think it's acceptable.
This is a perfect example of what Andrei recently posted: proposal of a language change for a pathetically limited special case. Your stride is not powerful enough to be worthwhile.
Feb 27 2009
prev sibling parent Sean Reque <seanthenewt yahoo.com> writes:
I think Ruby is a good example of a good way to implement intervals. Ruby
exposes them as a class object, so that you can instantiate them as
Range.new(min,max), and a Range object iterable and can easily be converted to
an array. It also supports inclusion testing. The only thing it doesn't do is
allow one to specify a step in the object itself, at least as of 1.8. (One CAN
step over a range with the step function, but this doesn't help for things like
inclusion testing). In addition to exposing a class interface that is
consistent with the rest of the language, the ruby language itself allows one
to define ranges using the .. notation as extra syntactic sugar.

I really like Ruby's combination of defining constructs in the language
consistently so that they looko the same as user-defined objects, and then
adding syntactic sugar on top to make the language more pleasant.  Regular
expressions are another example of how Ruby does this. Ruby allows one to
instantiate regular expressions the same way in as in python, as they are
implemented as a Regexp class, but Ruby also supports instantiating them using
perl's syntax, which underneath the hood is just creating a Regexp object. The
advantage of the perl-like syntax is that editors and IDEs can aid a user with
syntax highlighting, whereas when creating a regular expression with a string
no such help can be given.  

Doing things similarly in D, one could implement intervals in a library form in
a similar manner as Daniel did, and then add support in the language for the
syntax bearophile described. Of course, this would require that D would impose
that at least certain things got implemented in a standard library, whereas D
currently doesn't do if I understand correctly.. 
Feb 27 2009