"with" still sucks + removing features + adding features

bearophile bearophileHUGS at lycos.com
Mon May 18 17:25:26 PDT 2009


Andrei Alexandrescu:

Thank you for bringing a "real" example that gives something to work on.

>Awful!<

Well, one of your cases was wrong. Using the +1 at the end one of those cases become:
case 'A' .. 'Z'+1, 'a' .. 'z'+1:
Instead of what you have written:
case 'A' .. 'Z'+1: case 'a' .. 'z'+1:

I agree that that syntax with +1 isn't very nice looking. But the advantage of +1 is that it introduces (almost) no new syntax, it's not easy to miss, its meaning is easy to understand. AND you don't have to remember that in a case the .. is inclusive while in foreach is exclusive on the right, keeping the standard way in D to denote ranges.

-------------------------

We can introduce in D a syntax that maps on the semantics of iota(start, stop) or even iota(start, stop, step) (I don't remember if iota() supports opIn_r, and has length, if not then it's better to add them).

So you can do:

foreach (i; somerangesyntax) {...}
(So this replaces the current ranged foreach syntax).

case somerangesyntax: ...

if (x in somerangesyntax) {...}

map(fun, somerangesyntax);

And more, using only one (or two) general syntaxes.

-------------------------

The simple exclusive range syntax, with +1:

void classify(char c) {
    write("You passed ");
    switch (c) {
       case '#':
          writeln("a hash sign.");
          break;
       case '0' .. '9'+1:
          writeln("a digit.");
          break;
       case 'A' .. 'Z'+1, 'a' .. 'z'+1:
          writeln("an ASCII character.");
          break;
       case '.', ',', ':', ';', '!', '?':
          writeln("a punctuation mark.");
          break;
       default:
          writeln("quite a character!");
          break;
    }
}


Using Chapel closed range syntax:

void classify(char c) {
    write("You passed ");
    switch (c) {
       case '#':
          writeln("a hash sign.");
          break;
       case '0' ..# '9':
          writeln("a digit.");
          break;
       case 'A' ..# 'Z', 'a' ..# 'z':
          writeln("an ASCII character.");
          break;
       case '.', ',', ':', ';', '!', '?':
          writeln("a punctuation mark.");
          break;
       default:
          writeln("quite a character!");
          break;
    }
}


An alternative to that Chapel syntax:

void classify(char c) {
    write("You passed ");
    switch (c) {
       case '#':
          writeln("a hash sign.");
          break;
       case '0' ..> '9':
          writeln("a digit.");
          break;
       case 'A' ..> 'Z', 'a' ..> 'z':
          writeln("an ASCII character.");
          break;
       case '.', ',', ':', ';', '!', '?':
          writeln("a punctuation mark.");
          break;
       default:
          writeln("quite a character!");
          break;
    }
}


It may be easy to miss the third point (but it's not a noisy syntax)

void classify(char c) {
    write("You passed ");
    switch (c) {
       case '#':
          writeln("a hash sign.");
          break;
       case '0' ... '9':
          writeln("a digit.");
          break;
       case 'A' ... 'Z', 'a' ... 'z':
          writeln("an ASCII character.");
          break;
       case '.', ',', ':', ';', '!', '?':
          writeln("a punctuation mark.");
          break;
       default:
          writeln("quite a character!");
          break;
    }
}


Using something more explicit (interval() is lazy and inclusive on the right. The D compiler optimizes away at compile time the following calls):

void classify(char c) {
    write("You passed ");
    switch (c) {
       case '#':
          writeln("a hash sign.");
          break;
       case interval('0', '9'):
          writeln("a digit.");
          break;
       case interval('A', 'Z'), interval('a', 'z'):
          writeln("an ASCII character.");
          break;
       case '.', ',', ':', ';', '!', '?':
          writeln("a punctuation mark.");
          break;
       default:
          writeln("quite a character!");
          break;
    }
}


void classify(char c) {
    write("You passed ");
    switch (c) {
       case '#':
          writeln("a hash sign.");
          break;
       case ['0' .. '9']:
          writeln("a digit.");
          break;
       case ['A' .. 'Z'], ['a' .. 'z']:
          writeln("an ASCII character.");
          break;
       case '.', ',', ':', ';', '!', '?':
          writeln("a punctuation mark.");
          break;
       default:
          writeln("quite a character!");
          break;
    }
}



Such syntaxes are meant to be used in every other situation too (and I'd like to have another syntax that is non-inclusive, possibly like the current one):

foreach (i; 'A' .. 'Z'+1) {...}
case 'A' .. 'Z'+1: ...
if (x in 'A' .. 'Z'+1) {...}
map(fun, 'A' .. 'Z'+1);

foreach (i; 'A' ..# 'Z') {...}
case 'A' ..# 'Z': ...
if (x in 'A' ..# 'Z') {...}
map(fun, 'A' ..# 'Z');

foreach (i; 'A' ..> 'Z') {...}
case 'A' ..> 'Z': ...
if (x in 'A' ..> 'Z') {...}
map(fun, 'A' ..> 'Z');

foreach (i; 'A' ... 'Z') {...}
case 'A' ... 'Z': ...
if (x in 'A' ... 'Z') {...}
map(fun, 'A' ... 'Z');

foreach (i; interval('A', 'Z')) {...}
case interval('A', 'Z'): ...
if (x in interval('A', 'Z')) {...}
map(fun, interval('A', 'Z'));

foreach (i; ['A' .. 'Z']) {...}
case ['A' .. 'Z']: ...
if (x in ['A' .. 'Z']) {...}
map(fun, ['A' .. 'Z']);


The a..b+1 syntax can't be used inside map() in a simple way.

The [a .. b] syntax is acceptable, but then it doesn't offer a good way to define those if() and map() for the noninclusive situation:

// inclusive cases?
foreach (i; ['A' .. 'Z']) {...}
case ['A' .. 'Z']: ...
if (x in ['A' .. 'Z']) {...}
map(fun, ['A' .. 'Z']);

// noninclusive cases?
foreach (i; 'A' .. 'Z') {...}
case 'A' .. 'Z': ...
if (x in 'A' .. 'Z') {...}
map(fun, 'A' .. 'Z');

Well... That's not perfect, but it looks better than the syntax suggested by Andrei. Do you have better ideas?

Bye,
bearophile



More information about the Digitalmars-d mailing list