July 07, 2009
Derek Parnell wrote:
> On Mon, 06 Jul 2009 21:59:54 -0500, Andrei Alexandrescu wrote:
> 
>> There's a lot of punctuation that has different roles depending on the context. 
> 
> Agreed. No argument here. The meaning of punctuation depends on its
> context. Got it.
> 
> However, that aside, the syntax you have chosen will have a rational
> explanation for its superiority. So can you explain in simple terms why 
> 
>     CaseLabelInt .. CaseLabelInt  eg. case 1: .. case 9:
> 
> is superior than
> 
>     case CaseRange:  eg. case 1 .. 9:

It is superior because case 1 .. 9: raises the question on whether 9 is included or not. Consistency with a[1 .. 9] and foreach (x; 1 .. 9) suggests that 9 should not be included, common usage of case statements suggests that 9 should be included.

> given that
>   CaseLabelInt ==> case IntegerExpression :
>   CaseRange    ==> IntegerExpression .. IntegerExpression

What's this?

>> On the full side of the glass, with the latest dmd release, the language has acquired some useful feature improvements and the implementation has fixed many bugs.
> 
> Yes it truly has, and thank you very much to all contributors.
> 
>> Why the crankiness?
> 
> Because it is so demoralizing to point out 'warts' in D/DMD and be
> subsequently dismissed as ungrateful plebeians who are wasting the time of
> the patricians. Sorry, but you did ask.

I understand. Sorry about that. I am certain there's a misreading of attitude, though I clearly see how you could acquire that impression.


Andrei
July 07, 2009
The Anh Tran wrote:
> Andrei Alexandrescu wrote:
>> I agree there are ugly constructs in D, and is-expressions would near the top of the list (particularly the absolutely awful is(T : T[])), but you have no case (heh) with the switch statement.
>>
>>
>> Andrei
> 
> Just a funny suggestion: could we change the is() expression to imperative style?
> 
> Now:
> template Something(T, U, V) if ( is(T : T[]) && is(...) )
> {
>      alias T blah1;
>      U blah2;
>      class C {};
>      struct S {};
> }
> 
> In mirror universe:
> template Something(T, U, V)
> in
> {
>     static if ( T : T[] ) // static if here mean if (is (...))
>     {
>        // may do something with T, make alias, change type
>        // or check another constraint.
>     }
>     else
>     {
>         pragma(msg, "T must be Klingon");
>         static assert(false); // Or return false; ????
>     }
> 
>     static if ( U == int )
>     {
>        V = float; // ?????
>     }
>     return true;
> }
> body
> {
>      alias T blah1;
>      U blah2;
>      class C {};
>      struct S {};
> }

I wished for the longest time to simplify the often-used if(is(...)) syntax, but Walter said there are too many ambiguities involved if "is" gets dropped.

Andrei
July 07, 2009

Andrei Alexandrescu wrote:
> bearophile wrote:
>> Jason House:
>>> Hardly. There seemed to mostly be complaints about it with Andrei saying things like "I can't believe you don't see the elegance of the syntax". In the end, Andrei commented that he shouldn't involve the community in such small changes and went silent.<
>>
>> He was wrong. Even very intelligent people now and then do the wrong thing.
> 
> Of course the latter statement is true, but is in no way evidence supporting the former. About the former, in that particular case I was right.
> 
> Andrei

Now, now.  Let's all play nicely together...

I don't like the `case a:..case b:` syntax.  It doesn't matter.  The functionality is in place and the syntax has a sane explanation and rationale.

Unless there's some egregious problem aside from being a bit on the ugly side [1], it's just bike-shedding.  And I'm so, SOOO sick of bike-shedding.

Please, let's all just move on.


[1] like me.  My girlfriend disagrees with me on this, though.  *I* think she's crazy, but I'm not exactly inclined to try and change her mind.  :)
July 07, 2009
On Mon, 06 Jul 2009 21:29:43 -0700, Walter Bright wrote:

> Derek Parnell wrote:
>> However, that aside, the syntax you have chosen will have a rational explanation for its superiority. So can you explain in simple terms why
>> 
>>     CaseLabelInt .. CaseLabelInt  eg. case 1: .. case 9:
>> 
>> is superior than
>> 
>>     case CaseRange:  eg. case 1 .. 9:
>> 
>> given that
>>   CaseLabelInt ==> case IntegerExpression :
>>   CaseRange    ==> IntegerExpression .. IntegerExpression
> 
> Because
> 
> 1.	case X..Y:
> 
> looks like
> 
> 2.	foreach(e; X..Y)
> 3.	array[X..Y]
> 
> yet the X..Y has a VERY DIFFERENT meaning. (1) is inclusive of Y, and (2) and (3) are exclusive of Y.
> 
> Having a very different meaning means it should have a distinctly different syntax.

Thank you, but now I am confused ... Andrei just got through lecturing us that the meaning of punctuation is dependant upon context. So I think your example must be more like ...

 Because

 1.	case X..Y:

 looks like

 2.	foreach(e; X..Y)
 3.	array[X..Y]
 4.     case X:..caseY:


 yet the X..Y has a VERY DIFFERENT meaning. (1) is inclusive of Y, and
 (2) and (3) are exclusive of Y, and (4) is inclusive of Y ... oh, hang
on...

Sorry, but I'm just not getting the "VERY DIFFERENT" part yet. Right now, D
has ".." meaning exclude-end value (2. and 3.) AND it also has ".." meaning
include-end value (4.), depending on context.

Ok, I admit that there is one subtle difference. Examples 2 and 3 are of the form

  IntExpression .. IntExpression

and example 4 is

  CaseLabelInt .. CaseLabelInt

but seriously, people are not going to notice that. We see double-dot and think "range".

I know that this is not ever going to be changed so I'm not arguing that it should.

(One of the most frequent bugs I have in my D programs is that I forget that X..Y excludes Y because it's not natural to me to see text that looks like "the range X to Y" but means "the range X to Y-1".)

It seems that D would benefit from having a standard syntax format for
expressing various range sets;
 a. Include begin Include end, i.e. []
 b. Include begin Exclude end, i.e. [)
 c. Exclude begin Include end, i.e. (]
 d. Exclude begin Exclude end, i.e. ()

-- 
Derek Parnell
Melbourne, Australia
skype: derek.j.parnell
July 07, 2009
Robert Jacques wrote:
> On Mon, 06 Jul 2009 01:05:10 -0400, Walter Bright <newshound1@digitalmars.com> wrote:
> 
>> Something for everyone here.
>>
>>
>> http://www.digitalmars.com/d/1.0/changelog.html
>> http://ftp.digitalmars.com/dmd.1.046.zip
>>
>>
>> http://www.digitalmars.com/d/2.0/changelog.html
>> http://ftp.digitalmars.com/dmd.2.031.zip
> 
> Thanks for another great release.
> 
> Also, I'm not sure if this is a bug or a feature with regard to the new integer rules:
> 
>    byte x,y,z;
>    z = x+y;    // Error: cannot implicitly convert expression (cast(int)x + cast(int)y) of type int to byte
> 
> which makes sense, in that a byte can overflow, but also doesn't make sense, since integer behaviour is different.

Walter has implemented an ingenious scheme for disallowing narrowing conversions while at the same time minimizing the number of casts required. He hasn't explained it, so I'll sketch an explanation here.

The basic approach is "value range propagation": each expression is associated with a minimum possible value and a maximum possible value. As complex expressions are assembled out of simpler expressions, the ranges are computed and propagated.

For example, this code compiles:

int x = whatever();
bool y = x & 1;

The compiler figures that the range of x is int.min to int.max, the range of 1 is 1 to 1, and (here's the interesting part), the range of x & 1 is 0 to 1. So it lets the code go through. However, it won't allow this:

int x = whatever();
bool y = x & 2;

because x & 2 has range between 0 and 2, which won't fit in a bool.

The approach generalizes to arbitrary complex expressions. Now here's the trick though: the value range propagation is local, i.e. all ranges are forgotten beyond one expression. So as soon as you move on to the next statement, the ranges have been forgotten.

Why? Simply put, increased implementation difficulties and increased compiler memory footprint for diminishing returns. Both Walter and I noticed that expression-level value range propagation gets rid of all dangerous cases and the vast majority of required casts. Indeed, his test suite, Phobos, and my own codebase required surprisingly few changes with the new scheme. Moreover, we both discovered bugs due to the new feature, so we're happy with the status quo.

Now consider your code:

byte x,y,z;
z = x+y;

The first line initializes all values to zero. In an intra-procedural value range propagation, these zeros would be propagated to the next statement, which would range-check. However, in the current approach, the ranges of x, y, and z are forgotten at the first semicolon. Then, x+y has range -byte.min-byte.min up to byte.max+byte.max as far as the type checker knows. That would fit in a short (and by the way I just found a bug with that occasion) but not in a byte.

> BTW: The fact that in my original code base DMD gave me the line, inside the string, inside the mixin, inside the template, inside the mixin, inside the struct was just awesome.
> 
> P.S. There's a bunch of functions in phobos (like std.math.lrint) which return a long and should also have (at least) an integer version as well. (Maybe rndto!T() ?)

Sounds like Don's area. Don?


Andrei
July 07, 2009
Derek Parnell wrote:
>  Because
>   1.	case X..Y:
>   looks like
>   2.	foreach(e; X..Y)
>  3.	array[X..Y]
>  4.     case X:..caseY:
> 
> 
>  yet the X..Y has a VERY DIFFERENT meaning. (1) is inclusive of Y, and  (2) and (3) are exclusive of Y, and (4) is inclusive of Y ... oh, hang
> on...
> 
> Sorry, but I'm just not getting the "VERY DIFFERENT" part yet. Right now, D
> has ".." meaning exclude-end value (2. and 3.) AND it also has ".." meaning
> include-end value (4.), depending on context. 

I think here's the point where our thinking diverges. You say ".." has a meaning. No. It does not have any meaning. It's punctuation. Same way, "*", "&", ":", ",", etc. have no meaning in D all alone. They are tokens. They acquire a meaning by partaking in grammatical constructs.

What does have arguably a meaning is "expression1 .. expression2". (Even that could be debatable because it's not a standalone expression). But anyway, the meaning that "expression1 .. expression2" acquires in array slices and foreach statements is uniform.

Now we have the different construct "case expression1: .. case expression2:" That is evidently not "expression1 .. expression2", does not include it as a part, and shares essentially nothing except the ".." with it. That's my point.

> Ok, I admit that there is one subtle difference. Examples 2 and 3 are of
> the form 
> 
>   IntExpression .. IntExpression
> 
> and example 4 is 
> 
>   CaseLabelInt .. CaseLabelInt
> 
> but seriously, people are not going to notice that. We see double-dot and
> think "range". 

You should see "expression1 .. expression2" and think "range". There's very few tokens that make one think of only one thing when seeing them. One of them is "%" - makes one think of modulus, another is "^" meaning xor, another is "$" - array size... but that's about it.

> I know that this is not ever going to be changed so I'm not arguing that it
> should. 
> 
> (One of the most frequent bugs I have in my D programs is that I forget
> that X..Y excludes Y because it's not natural to me to see text that looks
> like "the range X to Y" but means "the range X to Y-1".)
> 
> It seems that D would benefit from having a standard syntax format for
> expressing various range sets;
>  a. Include begin Include end, i.e. []
>  b. Include begin Exclude end, i.e. [)
>  c. Exclude begin Include end, i.e. (]
>  d. Exclude begin Exclude end, i.e. ()

I'm afraid this would majorly mess with pairing of parens.


Andrei
July 07, 2009
On Tue, 07 Jul 2009 01:48:41 -0400, Andrei Alexandrescu <SeeWebsiteForEmail@erdani.org> wrote:

> Robert Jacques wrote:
>> On Mon, 06 Jul 2009 01:05:10 -0400, Walter Bright <newshound1@digitalmars.com> wrote:
>>
>>> Something for everyone here.
>>>
>>>
>>> http://www.digitalmars.com/d/1.0/changelog.html
>>> http://ftp.digitalmars.com/dmd.1.046.zip
>>>
>>>
>>> http://www.digitalmars.com/d/2.0/changelog.html
>>> http://ftp.digitalmars.com/dmd.2.031.zip
>>  Thanks for another great release.
>>  Also, I'm not sure if this is a bug or a feature with regard to the new integer rules:
>>     byte x,y,z;
>>    z = x+y;    // Error: cannot implicitly convert expression (cast(int)x + cast(int)y) of type int to byte
>>  which makes sense, in that a byte can overflow, but also doesn't make sense, since integer behaviour is different.
>
> Walter has implemented an ingenious scheme for disallowing narrowing conversions while at the same time minimizing the number of casts required. He hasn't explained it, so I'll sketch an explanation here.
>
> The basic approach is "value range propagation": each expression is associated with a minimum possible value and a maximum possible value. As complex expressions are assembled out of simpler expressions, the ranges are computed and propagated.
>
> For example, this code compiles:
>
> int x = whatever();
> bool y = x & 1;
>
> The compiler figures that the range of x is int.min to int.max, the range of 1 is 1 to 1, and (here's the interesting part), the range of x & 1 is 0 to 1. So it lets the code go through. However, it won't allow this:
>
> int x = whatever();
> bool y = x & 2;
>
> because x & 2 has range between 0 and 2, which won't fit in a bool.
>
> The approach generalizes to arbitrary complex expressions. Now here's the trick though: the value range propagation is local, i.e. all ranges are forgotten beyond one expression. So as soon as you move on to the next statement, the ranges have been forgotten.
>
> Why? Simply put, increased implementation difficulties and increased compiler memory footprint for diminishing returns. Both Walter and I noticed that expression-level value range propagation gets rid of all dangerous cases and the vast majority of required casts. Indeed, his test suite, Phobos, and my own codebase required surprisingly few changes with the new scheme. Moreover, we both discovered bugs due to the new feature, so we're happy with the status quo.
>
> Now consider your code:
>
> byte x,y,z;
> z = x+y;
>
> The first line initializes all values to zero. In an intra-procedural value range propagation, these zeros would be propagated to the next statement, which would range-check. However, in the current approach, the ranges of x, y, and z are forgotten at the first semicolon. Then, x+y has range -byte.min-byte.min up to byte.max+byte.max as far as the type checker knows. That would fit in a short (and by the way I just found a bug with that occasion) but not in a byte.

That's really cool. But I don't think that's actually happening (Or are these the bugs you're talking about?):

    byte x,y;
    short z;
    z = x+y;  // Error: cannot implicitly convert expression (cast(int)x + cast(int)y) of type int to short

    // Repeat for ubyte, bool, char, wchar and *, -, /

And by that logic shouldn't the following happen?

    int x,y;
    int z;
    z = x+y;  // Error: cannot implicitly convert expression (cast(long)x + cast(long)y) of type long to int

i.e. why the massive inconsistency between byte/short and int/long? (This is particularly a pain for generic i.e. templated code)

BTW: this means byte and short are not closed under arithmetic operations, which drastically limit their usefulness.
July 07, 2009
Ary Borenszweig wrote:
> のしいか (noshiika) escribió:
>> Thank you for the great work, Walter and all the other contributors.
>>
>> But I am a bit disappointed with the CaseRangeStatement syntax.
>> Why is it
>>    case 0: .. case 9:
>> instead of
>>    case 0 .. 9:
>>
>> With the latter notation, ranges can be easily used together with commas, for example:
>>    case 0, 2 .. 4, 6 .. 9:
>>
>> And CaseRangeStatement, being inconsistent with other syntaxes using the .. operator, i.e. slicing and ForeachRangeStatement, includes the endpoint.
>> Shouldn't D make use of another operator to express ranges that include the endpoints as Ruby or Perl6 does?
> 
> I agree.
> 
> I think this syntax is yet another one of those things people looking at D will say "ugly" and turn their heads away.


When the discussion first came up in the NG, I was a bit sceptical about Andrei's suggestion for the case range statement as well. Now, I definitely think it's the best choice, and it's only because I realised it can be written like this:

    case 1:
    ..
    case 4:
        // do stuff

Even though it's the same as case 1: .. case 4:, and even though adding those two newlines is just a visual change, it leaves (to me, at least) no doubt that this is an inclusive range even though the .. operator is used, simply because what I would otherwise write is:

    case 1:
    case 2:
    case 3:
    case 4:
        // do stuff

Also: Thanks for a great release, Walter, Andrei, Don, Sean and everyone else! (Who else are involved in core development of D2, by the way?) I am liking this language better and better the more I use it. And now, to convince the rest of the scientific community that FORTRAN must go...

-Lars
July 07, 2009
Hello Daniel,

> [1] like me. My girlfriend disagrees with me on this,

You have a girlfriend that even bothers to have an opinion on a programming issue, lucky bastard.

> though. *I* think she's crazy, but I'm not exactly
> inclined to try and change her mind. :)

That reminds me of a quote: "If you assume a woman's mind is supposed to work like a man's, the only conclusion you can come to is they are *all* crazy." OTOH you can switch the perspective on that around and I expect it's just as true. It should be pointed out that, almost by definition, you can't have 50% of the world be crazy.


July 07, 2009
On Tue, 07 Jul 2009 02:35:44 -0400, Robert Jacques <sandford@jhu.edu> wrote:

> On Tue, 07 Jul 2009 01:48:41 -0400, Andrei Alexandrescu <SeeWebsiteForEmail@erdani.org> wrote:
>
>> Robert Jacques wrote:
>>> On Mon, 06 Jul 2009 01:05:10 -0400, Walter Bright <newshound1@digitalmars.com> wrote:
>>>
>>>> Something for everyone here.
>>>>
>>>>
>>>> http://www.digitalmars.com/d/1.0/changelog.html
>>>> http://ftp.digitalmars.com/dmd.1.046.zip
>>>>
>>>>
>>>> http://www.digitalmars.com/d/2.0/changelog.html
>>>> http://ftp.digitalmars.com/dmd.2.031.zip
>>>  Thanks for another great release.
>>>  Also, I'm not sure if this is a bug or a feature with regard to the new integer rules:
>>>     byte x,y,z;
>>>    z = x+y;    // Error: cannot implicitly convert expression (cast(int)x + cast(int)y) of type int to byte
>>>  which makes sense, in that a byte can overflow, but also doesn't make sense, since integer behaviour is different.
>>
>> Walter has implemented an ingenious scheme for disallowing narrowing conversions while at the same time minimizing the number of casts required. He hasn't explained it, so I'll sketch an explanation here.
>>
>> The basic approach is "value range propagation": each expression is associated with a minimum possible value and a maximum possible value. As complex expressions are assembled out of simpler expressions, the ranges are computed and propagated.
>>
>> For example, this code compiles:
>>
>> int x = whatever();
>> bool y = x & 1;
>>
>> The compiler figures that the range of x is int.min to int.max, the range of 1 is 1 to 1, and (here's the interesting part), the range of x & 1 is 0 to 1. So it lets the code go through. However, it won't allow this:
>>
>> int x = whatever();
>> bool y = x & 2;
>>
>> because x & 2 has range between 0 and 2, which won't fit in a bool.
>>
>> The approach generalizes to arbitrary complex expressions. Now here's the trick though: the value range propagation is local, i.e. all ranges are forgotten beyond one expression. So as soon as you move on to the next statement, the ranges have been forgotten.
>>
>> Why? Simply put, increased implementation difficulties and increased compiler memory footprint for diminishing returns. Both Walter and I noticed that expression-level value range propagation gets rid of all dangerous cases and the vast majority of required casts. Indeed, his test suite, Phobos, and my own codebase required surprisingly few changes with the new scheme. Moreover, we both discovered bugs due to the new feature, so we're happy with the status quo.
>>
>> Now consider your code:
>>
>> byte x,y,z;
>> z = x+y;
>>
>> The first line initializes all values to zero. In an intra-procedural value range propagation, these zeros would be propagated to the next statement, which would range-check. However, in the current approach, the ranges of x, y, and z are forgotten at the first semicolon. Then, x+y has range -byte.min-byte.min up to byte.max+byte.max as far as the type checker knows. That would fit in a short (and by the way I just found a bug with that occasion) but not in a byte.
>
> That's really cool. But I don't think that's actually happening (Or are these the bugs you're talking about?):
>
>      byte x,y;
>      short z;
>      z = x+y;  // Error: cannot implicitly convert expression (cast(int)x + cast(int)y) of type int to short
>
>      // Repeat for ubyte, bool, char, wchar and *, -, /
>
> And by that logic shouldn't the following happen?
>
>      int x,y;
>      int z;
>      z = x+y;  // Error: cannot implicitly convert expression (cast(long)x + cast(long)y) of type long to int
>
> i.e. why the massive inconsistency between byte/short and int/long? (This is particularly a pain for generic i.e. templated code)
>
> BTW: this means byte and short are not closed under arithmetic operations, which drastically limit their usefulness.

Another inconsistency:

    byte[] x,y,z;
    z[] = x[]*y[]; // Compiles
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18