www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Switch: Case Range Syntax

reply Vijay Nayar <madric gmail.com> writes:
D adds a very handy feature that allows you to check for a range of 
values in a single case.  Is there a particular reason that the syntax 
"case <start>:  .. case <end>:" is used instead of treating the case 
statement similarly to an array slice, e.g. "case <start> .. <end>:"?

For example:

import std.stdio;

void main() {
    int bob = 12;
    switch (bob) {
        // Why not "case 0 .. 9:"?
        case 0: .. case 9:
            writeln("Less than 10.");
        case 10: .. case 19:
            writeln("Less than 20.");
        case 20: .. case 29:
            writeln("Less than 30.");
            break;
        default:
            break;
    }
    // Output:  Less than 20.  Less than 30.
}

 - Vijay
Aug 17 2011
next sibling parent reply "Lars T. Kyllingstad" <public kyllingen.NOSPAMnet> writes:
On Wed, 17 Aug 2011 17:27:43 +0000, Vijay Nayar wrote:

 D adds a very handy feature that allows you to check for a range of
 values in a single case.  Is there a particular reason that the syntax
 "case <start>:  .. case <end>:" is used instead of treating the case
 statement similarly to an array slice, e.g. "case <start> .. <end>:"?

This was discussed when the feature was introduced back in 2009. See this thread for details: http://www.digitalmars.com/d/archives/digitalmars/D/ Case_Range_Statement_.._92818.html Personally, I think case range statements make more sense (and look better) when they are typed like this: switch (someNumber) { case 0: .. case 9: writeln("Less than 10"); break; case 10: .. case 19: // and so on } -Lars
Aug 17 2011
parent reply Vijay Nayar <madric gmail.com> writes:
On Wed, 17 Aug 2011 17:40:40 +0000, Lars T. Kyllingstad wrote:

 On Wed, 17 Aug 2011 17:27:43 +0000, Vijay Nayar wrote:
 
 D adds a very handy feature that allows you to check for a range of
 values in a single case.  Is there a particular reason that the syntax
 "case <start>:  .. case <end>:" is used instead of treating the case
 statement similarly to an array slice, e.g. "case <start> .. <end>:"?

This was discussed when the feature was introduced back in 2009. See this thread for details: http://www.digitalmars.com/d/archives/digitalmars/D/ Case_Range_Statement_.._92818.html Personally, I think case range statements make more sense (and look better) when they are typed like this: switch (someNumber) { case 0: .. case 9: writeln("Less than 10"); break; case 10: .. case 19: // and so on } -Lars

Thanks for the post! This kind of history is pretty fascinating and I'll give it a good read. From what I've read so far, I'm surprised at how heated the debate becomes :) - Vijay
Aug 17 2011
parent bearophile <bearophileHUGS lycos.com> writes:
Vijay Nayar:

 Thanks for the post!  This kind of history is pretty fascinating and I'll 
 give it a good read.  From what I've read so far, I'm surprised at how 
 heated the debate becomes :)

The current syntax is a compromise of several different needs, and it's acceptable, despite a minor fault (it reminds a bit too much the slice syntax, despite including the final item. The final item is included because it is more handy for char ranges: you are allowed to write case 'a': .. case 'z': to take all lowercase ASCII). I'd like some support for final switches on integers, and to use range case syntax in final switches too. Bye, bearophile
Aug 17 2011
prev sibling next sibling parent reply "Jonathan M Davis" <jmdavisProg gmx.com> writes:
On Wednesday, August 17, 2011 10:27 Vijay Nayar wrote:
 D adds a very handy feature that allows you to check for a range of
 values in a single case. Is there a particular reason that the syntax
 "case <start>: .. case <end>:" is used instead of treating the case
 statement similarly to an array slice, e.g. "case <start> .. <end>:"?
 
 For example:
 
 import std.stdio;
 
 void main() {
 int bob = 12;
 switch (bob) {
 // Why not "case 0 .. 9:"?
 case 0: .. case 9:
 writeln("Less than 10.");
 case 10: .. case 19:
 writeln("Less than 20.");
 case 20: .. case 29:
 writeln("Less than 30.");
 break;
 default:
 break;
 }
 // Output: Less than 20. Less than 30.
 }

I don't know, but ranged case statements don't have the same semantics as giving a range of values when slicing or to a foreach loop, so that may be why. arr[0 .. 10] does _not_ include the element at index 10. case 0: case 10: _does_ include 10. So, it actually probably be a bad thing for them to use the same syntax. To use the same syntax for both would make the semantics of that syntax inconsistent and confusing. - Jonathan M Davis
Aug 17 2011
parent reply Jacob Carlborg <doob me.com> writes:
On 2011-08-17 19:48, Jonathan M Davis wrote:
 On Wednesday, August 17, 2011 10:27 Vijay Nayar wrote:
 D adds a very handy feature that allows you to check for a range of
 values in a single case. Is there a particular reason that the syntax
 "case<start>: .. case<end>:" is used instead of treating the case
 statement similarly to an array slice, e.g. "case<start>  ..<end>:"?

 For example:

 import std.stdio;

 void main() {
 int bob = 12;
 switch (bob) {
 // Why not "case 0 .. 9:"?
 case 0: .. case 9:
 writeln("Less than 10.");
 case 10: .. case 19:
 writeln("Less than 20.");
 case 20: .. case 29:
 writeln("Less than 30.");
 break;
 default:
 break;
 }
 // Output: Less than 20. Less than 30.
 }

I don't know, but ranged case statements don't have the same semantics as giving a range of values when slicing or to a foreach loop, so that may be why. arr[0 .. 10] does _not_ include the element at index 10. case 0: case 10: _does_ include 10. So, it actually probably be a bad thing for them to use the same syntax. To use the same syntax for both would make the semantics of that syntax inconsistent and confusing. - Jonathan M Davis

D should have a built-in range type. One that supports syntax for both including and excluding the last element: auto a = 3 .. 5 auto b = 3 ... 5 Then we wouldn't need a special range syntax for switch statements. You could store ranges in variables and pass them to functions. opSlice probably wouldn't be needed, instead opIndex could be used and you would declare the method to take a range instead of two integers. -- /Jacob Carlborg
Aug 17 2011
next sibling parent simendsjo <simendsjo gmail.com> writes:
On 17.08.2011 21:35, Jacob Carlborg wrote:
 On 2011-08-17 19:48, Jonathan M Davis wrote:
 On Wednesday, August 17, 2011 10:27 Vijay Nayar wrote:
 D adds a very handy feature that allows you to check for a range of
 values in a single case. Is there a particular reason that the syntax
 "case<start>: .. case<end>:" is used instead of treating the case
 statement similarly to an array slice, e.g. "case<start> ..<end>:"?

 For example:

 import std.stdio;

 void main() {
 int bob = 12;
 switch (bob) {
 // Why not "case 0 .. 9:"?
 case 0: .. case 9:
 writeln("Less than 10.");
 case 10: .. case 19:
 writeln("Less than 20.");
 case 20: .. case 29:
 writeln("Less than 30.");
 break;
 default:
 break;
 }
 // Output: Less than 20. Less than 30.
 }

I don't know, but ranged case statements don't have the same semantics as giving a range of values when slicing or to a foreach loop, so that may be why. arr[0 .. 10] does _not_ include the element at index 10. case 0: case 10: _does_ include 10. So, it actually probably be a bad thing for them to use the same syntax. To use the same syntax for both would make the semantics of that syntax inconsistent and confusing. - Jonathan M Davis

D should have a built-in range type. One that supports syntax for both including and excluding the last element: auto a = 3 .. 5 auto b = 3 ... 5 Then we wouldn't need a special range syntax for switch statements. You could store ranges in variables and pass them to functions. opSlice probably wouldn't be needed, instead opIndex could be used and you would declare the method to take a range instead of two integers.

int a = 3 .. 5; No need to have run-time assertions for >= 3 && <= 5 Don't we all like compile-time errors?
Aug 17 2011
prev sibling next sibling parent Robert Clipsham <robert octarineparrot.com> writes:
On 17/08/2011 20:35, Jacob Carlborg wrote:
 D should have a built-in range type.

+1 or maybe +2 if I get an extra vote ;)
 One that supports syntax for both
 including and excluding the last element:

 auto a = 3 .. 5
 auto b = 3 ... 5

<begin bikeshedding>
 Then we wouldn't need a special range syntax for switch statements. You
 could store ranges in variables and pass them to functions. opSlice
 probably wouldn't be needed, instead opIndex could be used and you would
 declare the method to take a range instead of two integers.

-- Robert http://octarineparrot.com/
Aug 17 2011
prev sibling next sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 08/17/2011 09:35 PM, Jacob Carlborg wrote:
 On 2011-08-17 19:48, Jonathan M Davis wrote:
 On Wednesday, August 17, 2011 10:27 Vijay Nayar wrote:
 D adds a very handy feature that allows you to check for a range of
 values in a single case. Is there a particular reason that the syntax
 "case<start>: .. case<end>:" is used instead of treating the case
 statement similarly to an array slice, e.g. "case<start> ..<end>:"?

 For example:

 import std.stdio;

 void main() {
 int bob = 12;
 switch (bob) {
 // Why not "case 0 .. 9:"?
 case 0: .. case 9:
 writeln("Less than 10.");
 case 10: .. case 19:
 writeln("Less than 20.");
 case 20: .. case 29:
 writeln("Less than 30.");
 break;
 default:
 break;
 }
 // Output: Less than 20. Less than 30.
 }

I don't know, but ranged case statements don't have the same semantics as giving a range of values when slicing or to a foreach loop, so that may be why. arr[0 .. 10] does _not_ include the element at index 10. case 0: case 10: _does_ include 10. So, it actually probably be a bad thing for them to use the same syntax. To use the same syntax for both would make the semantics of that syntax inconsistent and confusing. - Jonathan M Davis

D should have a built-in range type. One that supports syntax for both including and excluding the last element: auto a = 3 .. 5 auto b = 3 ... 5 Then we wouldn't need a special range syntax for switch statements. You could store ranges in variables and pass them to functions. opSlice probably wouldn't be needed, instead opIndex could be used and you would declare the method to take a range instead of two integers.

The .. 'operator' is the operator with the lowest precedence in D (it binds even less strongly than ?:), so there is no way auto a = 3 .. 5 can ever work without a breaking language change. But again, 'case range:' matches 'range', not each individual element of 'range' so you'd actually still need a special range syntax for case statements.
Aug 17 2011
parent Jacob Carlborg <doob me.com> writes:
On 2011-08-17 21:58, Timon Gehr wrote:
 On 08/17/2011 09:35 PM, Jacob Carlborg wrote:
 D should have a built-in range type. One that supports syntax for both
 including and excluding the last element:

 auto a = 3 .. 5
 auto b = 3 ... 5

 Then we wouldn't need a special range syntax for switch statements. You
 could store ranges in variables and pass them to functions. opSlice
 probably wouldn't be needed, instead opIndex could be used and you would
 declare the method to take a range instead of two integers.

The .. 'operator' is the operator with the lowest precedence in D (it binds even less strongly than ?:), so there is no way auto a = 3 .. 5 can ever work without a breaking language change.

I don't see either how this could be changed without a breaking language change.
 But again, 'case range:' matches 'range', not each individual element of
 'range' so you'd actually still need a special range syntax for case
 statements.

Wouldn't that depend on how it's implemented. -- /Jacob Carlborg
Aug 18 2011
prev sibling next sibling parent bearophile <bearophileHUGS lycos.com> writes:
Jacob Carlborg:

 D should have a built-in range type. One that supports syntax for both 
 including and excluding the last element:
 
 auto a = 3 .. 5
 auto b = 3 ... 5
 
 Then we wouldn't need a special range syntax for switch statements.

Regarding just switches, GCC has a non standard syntax extension that allows you to write case 'a' ... 'z': http://gcc.gnu.org/onlinedocs/gcc-3.0.2/gcc_5.html#SEC90 But it also says:
 Note: Always write spaces around the ..., for otherwise it may be parsed wrong
 when you use it with integer values. For example, write this:
 case 1 ... 5:
 rather than this:
 case 1...5:

Bye, bearophile
Aug 17 2011
prev sibling next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 8/17/11 2:35 PM, Jacob Carlborg wrote:
 On 2011-08-17 19:48, Jonathan M Davis wrote:
 On Wednesday, August 17, 2011 10:27 Vijay Nayar wrote:
 D adds a very handy feature that allows you to check for a range of
 values in a single case. Is there a particular reason that the syntax
 "case<start>: .. case<end>:" is used instead of treating the case
 statement similarly to an array slice, e.g. "case<start> ..<end>:"?

 For example:

 import std.stdio;

 void main() {
 int bob = 12;
 switch (bob) {
 // Why not "case 0 .. 9:"?
 case 0: .. case 9:
 writeln("Less than 10.");
 case 10: .. case 19:
 writeln("Less than 20.");
 case 20: .. case 29:
 writeln("Less than 30.");
 break;
 default:
 break;
 }
 // Output: Less than 20. Less than 30.
 }

I don't know, but ranged case statements don't have the same semantics as giving a range of values when slicing or to a foreach loop, so that may be why. arr[0 .. 10] does _not_ include the element at index 10. case 0: case 10: _does_ include 10. So, it actually probably be a bad thing for them to use the same syntax. To use the same syntax for both would make the semantics of that syntax inconsistent and confusing. - Jonathan M Davis

D should have a built-in range type. One that supports syntax for both including and excluding the last element: auto a = 3 .. 5 auto b = 3 ... 5 Then we wouldn't need a special range syntax for switch statements. You could store ranges in variables and pass them to functions. opSlice probably wouldn't be needed, instead opIndex could be used and you would declare the method to take a range instead of two integers.

I doubt that would work well. Let's ignore for now mundane issues such as the ambiguity of 3...5 and focus on something like: int x; ... switch (x) { case 3 ... 5: return 1; default: return 0; } We'd need to change the behavior of switch anyway, or we'd need to define equality such that e.g. 4 == 3 ... 5. But then equality is not transitive anymore because 4 == 2 ... 6 too, but 3 ... 5 is not equal to 2 ... 6. Adding new built-in types is not easy. For a variety of reasons we should move the other way (per e.g. the hashtables discussion elsethread). Andrei
Aug 17 2011
next sibling parent reply Jacob Carlborg <doob me.com> writes:
On 2011-08-18 00:51, Andrei Alexandrescu wrote:
 On 8/17/11 2:35 PM, Jacob Carlborg wrote:
 D should have a built-in range type. One that supports syntax for both
 including and excluding the last element:

 auto a = 3 .. 5
 auto b = 3 ... 5

 Then we wouldn't need a special range syntax for switch statements. You
 could store ranges in variables and pass them to functions. opSlice
 probably wouldn't be needed, instead opIndex could be used and you would
 declare the method to take a range instead of two integers.

I doubt that would work well. Let's ignore for now mundane issues such as the ambiguity of 3...5 and focus on something like:

I'm just hoping that floating point syntax will be removed. I know a lot of people agree with me on this.
 int x;
 ...
 switch (x) {
 case 3 ... 5: return 1;
 default: return 0;
 }

 We'd need to change the behavior of switch anyway, or we'd need to
 define equality such that e.g. 4 == 3 ... 5. But then equality is not
 transitive anymore because 4 == 2 ... 6 too, but 3 ... 5 is not equal to
 2 ... 6.

There could be some kind of "include" function/property or similar. Just as there are built-in properties on other types in D.
 Adding new built-in types is not easy. For a variety of reasons we
 should move the other way (per e.g. the hashtables discussion elsethread).


 Andrei

I know that the implementation of the associative arrays have been moved to the runtime, if you're referring to that. But the compiler still knows about the associative array, right? I don't care where a range type would be implemented, in the compiler or in the runtime. -- /Jacob Carlborg
Aug 18 2011
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 8/18/11 6:42 AM, Jacob Carlborg wrote:
 On 2011-08-18 00:51, Andrei Alexandrescu wrote:
 On 8/17/11 2:35 PM, Jacob Carlborg wrote:
 D should have a built-in range type. One that supports syntax for both
 including and excluding the last element:

 auto a = 3 .. 5
 auto b = 3 ... 5

 Then we wouldn't need a special range syntax for switch statements. You
 could store ranges in variables and pass them to functions. opSlice
 probably wouldn't be needed, instead opIndex could be used and you would
 declare the method to take a range instead of two integers.

I doubt that would work well. Let's ignore for now mundane issues such as the ambiguity of 3...5 and focus on something like:

I'm just hoping that floating point syntax will be removed. I know a lot of people agree with me on this.
 int x;
 ...
 switch (x) {
 case 3 ... 5: return 1;
 default: return 0;
 }

 We'd need to change the behavior of switch anyway, or we'd need to
 define equality such that e.g. 4 == 3 ... 5. But then equality is not
 transitive anymore because 4 == 2 ... 6 too, but 3 ... 5 is not equal to
 2 ... 6.

There could be some kind of "include" function/property or similar. Just as there are built-in properties on other types in D.

There could be indeed a lot of things. My point is that there _need_ to be such. It's not a simple, minute addition.
 Adding new built-in types is not easy. For a variety of reasons we
 should move the other way (per e.g. the hashtables discussion
 elsethread).


 Andrei

I know that the implementation of the associative arrays have been moved to the runtime, if you're referring to that. But the compiler still knows about the associative array, right? I don't care where a range type would be implemented, in the compiler or in the runtime.

I'm not talking about an implementation difficulty, but about semantics difficulties within the language. My point is that although the feature superficially looks easy to add and nice to have, in reality there are a lot of subtle additional changes we must make to the language semantics. Andrei
Aug 18 2011
parent Jacob Carlborg <doob me.com> writes:
On 2011-08-18 15:36, Andrei Alexandrescu wrote:
 On 8/18/11 6:42 AM, Jacob Carlborg wrote:
 On 2011-08-18 00:51, Andrei Alexandrescu wrote:
 On 8/17/11 2:35 PM, Jacob Carlborg wrote:
 D should have a built-in range type. One that supports syntax for both
 including and excluding the last element:

 auto a = 3 .. 5
 auto b = 3 ... 5

 Then we wouldn't need a special range syntax for switch statements. You
 could store ranges in variables and pass them to functions. opSlice
 probably wouldn't be needed, instead opIndex could be used and you
 would
 declare the method to take a range instead of two integers.

I doubt that would work well. Let's ignore for now mundane issues such as the ambiguity of 3...5 and focus on something like:

I'm just hoping that floating point syntax will be removed. I know a lot of people agree with me on this.
 int x;
 ...
 switch (x) {
 case 3 ... 5: return 1;
 default: return 0;
 }

 We'd need to change the behavior of switch anyway, or we'd need to
 define equality such that e.g. 4 == 3 ... 5. But then equality is not
 transitive anymore because 4 == 2 ... 6 too, but 3 ... 5 is not equal to
 2 ... 6.

There could be some kind of "include" function/property or similar. Just as there are built-in properties on other types in D.

There could be indeed a lot of things. My point is that there _need_ to be such. It's not a simple, minute addition.
 Adding new built-in types is not easy. For a variety of reasons we
 should move the other way (per e.g. the hashtables discussion
 elsethread).


 Andrei

I know that the implementation of the associative arrays have been moved to the runtime, if you're referring to that. But the compiler still knows about the associative array, right? I don't care where a range type would be implemented, in the compiler or in the runtime.

I'm not talking about an implementation difficulty, but about semantics difficulties within the language. My point is that although the feature superficially looks easy to add and nice to have, in reality there are a lot of subtle additional changes we must make to the language semantics. Andrei

Ok, we can stop this discussion. In my original post I was just trying to say that it's a pity that D doesn't have a built-in range type and if it was three, four years ago it could have been added to the language, perhaps. I could have avoided a few, in my opinion, "hacks", like the case ranges. I fully understand if this feature cannot be added to the language now. -- /Jacob Carlborg
Aug 18 2011
prev sibling next sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 8/18/11 10:02 AM, Vijay Nayar wrote:
 On Wed, 17 Aug 2011 17:51:48 -0500, Andrei Alexandrescu wrote:

 I doubt that would work well. Let's ignore for now mundane issues such
 as the ambiguity of 3...5 and focus on something like:

 int x;
 ...
 switch (x) {
 case 3 ... 5: return 1;
 default: return 0;
 }

 We'd need to change the behavior of switch anyway, or we'd need to
 define equality such that e.g. 4 == 3 ... 5. But then equality is not
 transitive anymore because 4 == 2 ... 6 too, but 3 ... 5 is not equal to
 2 ... 6.

 Adding new built-in types is not easy. For a variety of reasons we
 should move the other way (per e.g. the hashtables discussion
 elsethread).


 Andrei

From this discussion, I gather that the 'switch' statement is effectively using '==' to compare to each 'case' statement value (I didn't know that's how it worked). Andrei, you are very good at explaining concepts in plain English, and I was hoping you could explain what D is seeing when it sees the 'case A: .. case B:' syntax. My current understanding is that 'switch' is effectively iterating through all it's top-level tokens, which are 'case', '..', and 'default', in a manner similar to a for-loop. The special top-level token '..' is interpreted to effectively turn into new 'case' tokens starting from the last seen 'case' and ending when the next 'case' is seen. Is that correct? Does the D 'switch' statement replace the '..' token with a bunch of 'case' tokens, thus allowing the 'swtich' hash-lookup to work quickly and efficiently? - Vijay

The exact behavior is dependent on the compiler implementation. Essentially, yes, switch is recommended when you have branches of comparable probability, but don't read too much into specific implementation mechanisms, and don't mix language semantics with grammar minutia. Andrei
Aug 18 2011
prev sibling parent Don <nospam nospam.com> writes:
Vijay Nayar wrote:
 On Wed, 17 Aug 2011 17:51:48 -0500, Andrei Alexandrescu wrote:
 
 I doubt that would work well. Let's ignore for now mundane issues such
 as the ambiguity of 3...5 and focus on something like:

 int x;
 ...
 switch (x) {
 case 3 ... 5: return 1;
 default: return 0;
 }

 We'd need to change the behavior of switch anyway, or we'd need to
 define equality such that e.g. 4 == 3 ... 5. But then equality is not
 transitive anymore because 4 == 2 ... 6 too, but 3 ... 5 is not equal to
 2 ... 6.

 Adding new built-in types is not easy. For a variety of reasons we
 should move the other way (per e.g. the hashtables discussion
 elsethread).


 Andrei

From this discussion, I gather that the 'switch' statement is effectively using '==' to compare to each 'case' statement value (I didn't know that's how it worked). Andrei, you are very good at explaining concepts in plain English, and I was hoping you could explain what D is seeing when it sees the 'case A: .. case B:' syntax. My current understanding is that 'switch' is effectively iterating through all it's top-level tokens, which are 'case', '..', and 'default', in a manner similar to a for-loop. The special top-level token '..' is interpreted to effectively turn into new 'case' tokens starting from the last seen 'case' and ending when the next 'case' is seen. Is that correct? Does the D 'switch' statement replace the '..' token with a bunch of 'case' tokens, thus allowing the 'swtich' hash-lookup to work quickly and efficiently?

Yes. That's why the maximum length of the range is currently limited to 256 cases. Something like: case 0: .. case int.max: break; won't currently compile.
Aug 18 2011
prev sibling next sibling parent Jacob Carlborg <doob me.com> writes:
On 2011-08-17 22:00, Jonathan M Davis wrote:
 On Wednesday, August 17, 2011 12:35 Jacob Carlborg wrote:
 On 2011-08-17 19:48, Jonathan M Davis wrote:
 On Wednesday, August 17, 2011 10:27 Vijay Nayar wrote:
 D adds a very handy feature that allows you to check for a range of
 values in a single case. Is there a particular reason that the syntax
 "case<start>: .. case<end>:" is used instead of treating the case
 statement similarly to an array slice, e.g. "case<start>  ..<end>:"?

 For example:

 import std.stdio;

 void main() {
 int bob = 12;
 switch (bob) {
 // Why not "case 0 .. 9:"?
 case 0: .. case 9:
 writeln("Less than 10.");
 case 10: .. case 19:
 writeln("Less than 20.");
 case 20: .. case 29:
 writeln("Less than 30.");
 break;
 default:
 break;
 }
 // Output: Less than 20. Less than 30.
 }

I don't know, but ranged case statements don't have the same semantics as giving a range of values when slicing or to a foreach loop, so that may be why. arr[0 .. 10] does _not_ include the element at index 10. case 0: case 10: _does_ include 10. So, it actually probably be a bad thing for them to use the same syntax. To use the same syntax for both would make the semantics of that syntax inconsistent and confusing. - Jonathan M Davis

D should have a built-in range type. One that supports syntax for both including and excluding the last element: auto a = 3 .. 5 auto b = 3 ... 5 Then we wouldn't need a special range syntax for switch statements. You could store ranges in variables and pass them to functions. opSlice probably wouldn't be needed, instead opIndex could be used and you would declare the method to take a range instead of two integers.

I suggest that you read the thread that Lars linked to. That type of syntax got shot down essentially for being too easy to confuse .. with ... making it hard to read and easy to screw-up. Regardless, the whole issue got discussed ad naseum there, and the situation definitely isn't going to change for D2, so there really isn't much point in arguing the pros and cons at this point. Maybe there will be something similar in D3 if someone can find an appropriately clear syntax for it, but I'd be very surprised if any such thing happened in D2. - Jonathan M Davis

Yes I know that it has been discussed before. We don't have to discuss this again, I'm just telling my opinion and I don't agree with .. and ... being confusing. -- /Jacob Carlborg
Aug 18 2011
prev sibling parent Jacob Carlborg <doob me.com> writes:
On 2011-08-17 22:00, Jonathan M Davis wrote:
 I suggest that you read the thread that Lars linked to. That type of syntax
 got shot down essentially for being too easy to confuse .. with ... making it
 hard to read and easy to screw-up. Regardless, the whole issue got discussed
 ad naseum there, and the situation definitely isn't going to change for D2, so
 there really isn't much point in arguing the pros and cons at this point.
 Maybe there will be something similar in D3 if someone can find an
 appropriately clear syntax for it, but I'd be very surprised if any such thing
 happened in D2.

 - Jonathan M Davis

BTW, I think it would be more useful to have a built-in type for ranges then syntax for both inclusive and exclusive end. -- /Jacob Carlborg
Aug 18 2011
prev sibling next sibling parent Timon Gehr <timon.gehr gmx.ch> writes:
On 08/17/2011 07:27 PM, Vijay Nayar wrote:
 D adds a very handy feature that allows you to check for a range of
 values in a single case.  Is there a particular reason that the syntax
 "case<start>:  .. case<end>:" is used instead of treating the case
 statement similarly to an array slice, e.g. "case<start>  ..<end>:"?

 For example:

 import std.stdio;

 void main() {
      int bob = 12;
      switch (bob) {
          // Why not "case 0 .. 9:"?
          case 0: .. case 9:
              writeln("Less than 10.");
          case 10: .. case 19:
              writeln("Less than 20.");
          case 20: .. case 29:
              writeln("Less than 30.");
              break;
          default:
              break;
      }
      // Output:  Less than 20.  Less than 30.
 }

   - Vijay

With the other syntax, the wrapping switch statement really should look like this: switch(a .. b){ case x .. y: // a case statement that matches a range } as opposed to: switch(a){ case x: .. case y: // a range of case statements }
Aug 17 2011
prev sibling next sibling parent "Jonathan M Davis" <jmdavisProg gmx.com> writes:
On Wednesday, August 17, 2011 12:35 Jacob Carlborg wrote:
 On 2011-08-17 19:48, Jonathan M Davis wrote:
 On Wednesday, August 17, 2011 10:27 Vijay Nayar wrote:
 D adds a very handy feature that allows you to check for a range of
 values in a single case. Is there a particular reason that the syntax
 "case<start>: .. case<end>:" is used instead of treating the case
 statement similarly to an array slice, e.g. "case<start> ..<end>:"?
 
 For example:
 
 import std.stdio;
 
 void main() {
 int bob = 12;
 switch (bob) {
 // Why not "case 0 .. 9:"?
 case 0: .. case 9:
 writeln("Less than 10.");
 case 10: .. case 19:
 writeln("Less than 20.");
 case 20: .. case 29:
 writeln("Less than 30.");
 break;
 default:
 break;
 }
 // Output: Less than 20. Less than 30.
 }

I don't know, but ranged case statements don't have the same semantics as giving a range of values when slicing or to a foreach loop, so that may be why. arr[0 .. 10] does _not_ include the element at index 10. case 0: case 10: _does_ include 10. So, it actually probably be a bad thing for them to use the same syntax. To use the same syntax for both would make the semantics of that syntax inconsistent and confusing. - Jonathan M Davis

D should have a built-in range type. One that supports syntax for both including and excluding the last element: auto a = 3 .. 5 auto b = 3 ... 5 Then we wouldn't need a special range syntax for switch statements. You could store ranges in variables and pass them to functions. opSlice probably wouldn't be needed, instead opIndex could be used and you would declare the method to take a range instead of two integers.

I suggest that you read the thread that Lars linked to. That type of syntax got shot down essentially for being too easy to confuse .. with ... making it hard to read and easy to screw-up. Regardless, the whole issue got discussed ad naseum there, and the situation definitely isn't going to change for D2, so there really isn't much point in arguing the pros and cons at this point. Maybe there will be something similar in D3 if someone can find an appropriately clear syntax for it, but I'd be very surprised if any such thing happened in D2. - Jonathan M Davis
Aug 17 2011
prev sibling next sibling parent Vijay Nayar <madric gmail.com> writes:
On Wed, 17 Aug 2011 16:29:19 -0400, bearophile wrote:

 Jacob Carlborg:
 
 D should have a built-in range type. One that supports syntax for both
 including and excluding the last element:
 
 auto a = 3 .. 5
 auto b = 3 ... 5
 
 Then we wouldn't need a special range syntax for switch statements.

Regarding just switches, GCC has a non standard syntax extension that allows you to write case 'a' ... 'z': http://gcc.gnu.org/onlinedocs/gcc-3.0.2/gcc_5.html#SEC90 But it also says:
 Note: Always write spaces around the ..., for otherwise it may be
 parsed wrong when you use it with integer values. For example, write
 this: case 1 ... 5:
 rather than this:
 case 1...5:

Bye, bearophile

That's pretty clever. They recognized the ambiguity problem of misinterpreting a string like "5...5" as either "5. .. 5", "5 .. .5" or something else by requiring spaces around the '...' operator. Quoting from the above link:
 Be careful: Write spaces around the ..., for otherwise it may be parsed
 wrong when you use it with integer values. For example, write this:
   case 1 ... 5:
 rather than this:
   case 1...5:

Or maybe I'm just easily impressed :) What can I say, it was the death- by-a-thousand-cuts in C++ that pushed to find a better compiled language in the first place. - Vijay
Aug 17 2011
prev sibling next sibling parent Vijay Nayar <madric gmail.com> writes:
On Wed, 17 Aug 2011 17:51:48 -0500, Andrei Alexandrescu wrote:

 
 I doubt that would work well. Let's ignore for now mundane issues such
 as the ambiguity of 3...5 and focus on something like:
 
 int x;
 ...
 switch (x) {
 case 3 ... 5: return 1;
 default: return 0;
 }
 
 We'd need to change the behavior of switch anyway, or we'd need to
 define equality such that e.g. 4 == 3 ... 5. But then equality is not
 transitive anymore because 4 == 2 ... 6 too, but 3 ... 5 is not equal to
 2 ... 6.
 
 Adding new built-in types is not easy. For a variety of reasons we
 should move the other way (per e.g. the hashtables discussion
 elsethread).
 
 
 Andrei

From this discussion, I gather that the 'switch' statement is effectively using '==' to compare to each 'case' statement value (I didn't know that's how it worked). Andrei, you are very good at explaining concepts in plain English, and I was hoping you could explain what D is seeing when it sees the 'case A: .. case B:' syntax. My current understanding is that 'switch' is effectively iterating through all it's top-level tokens, which are 'case', '..', and 'default', in a manner similar to a for-loop. The special top-level token '..' is interpreted to effectively turn into new 'case' tokens starting from the last seen 'case' and ending when the next 'case' is seen. Is that correct? Does the D 'switch' statement replace the '..' token with a bunch of 'case' tokens, thus allowing the 'swtich' hash-lookup to work quickly and efficiently? - Vijay
Aug 18 2011
prev sibling parent "Marco Leise" <Marco.Leise gmx.de> writes:
Am 17.08.2011, 21:49 Uhr, schrieb simendsjo <simendsjo gmail.com>:

 On 17.08.2011 21:35, Jacob Carlborg wrote:
 On 2011-08-17 19:48, Jonathan M Davis wrote:
 On Wednesday, August 17, 2011 10:27 Vijay Nayar wrote:
 D adds a very handy feature that allows you to check for a range of
 values in a single case. Is there a particular reason that the syntax
 "case<start>: .. case<end>:" is used instead of treating the case
 statement similarly to an array slice, e.g. "case<start> ..<end>:"?

 For example:

 import std.stdio;

 void main() {
 int bob = 12;
 switch (bob) {
 // Why not "case 0 .. 9:"?
 case 0: .. case 9:
 writeln("Less than 10.");
 case 10: .. case 19:
 writeln("Less than 20.");
 case 20: .. case 29:
 writeln("Less than 30.");
 break;
 default:
 break;
 }
 // Output: Less than 20. Less than 30.
 }

I don't know, but ranged case statements don't have the same semantics as giving a range of values when slicing or to a foreach loop, so that may be why. arr[0 .. 10] does _not_ include the element at index 10. case 0: case 10: _does_ include 10. So, it actually probably be a bad thing for them to use the same syntax. To use the same syntax for both would make the semantics of that syntax inconsistent and confusing. - Jonathan M Davis

D should have a built-in range type. One that supports syntax for both including and excluding the last element: auto a = 3 .. 5 auto b = 3 ... 5 Then we wouldn't need a special range syntax for switch statements. You could store ranges in variables and pass them to functions. opSlice probably wouldn't be needed, instead opIndex could be used and you would declare the method to take a range instead of two integers.

int a = 3 .. 5; No need to have run-time assertions for >= 3 && <= 5 Don't we all like compile-time errors?

Delphi uses ranges in a way similar to declaring an enum: type TDigit = 0 .. 9; You can then use that in a set, which translates to a Phobos "BitArray": type TDigitSet = set of TDigit; This is now a type that is at least 10 bit long and the usual set operations apply. I liked it to declare types that restrict the set of available values, like a byte but only with the numbers 0 to 100 available. I think it could work like the range checks on arrays. But we are going off-topic here :p
Aug 18 2011