www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Thoughts on some code breakage with 2.074

reply Brian Schott <briancschott gmail.com> writes:
Recently the EMSI data department upgraded the compiler we use to 
build our data processing code to 2.074. This caused several of 
the thousands of processes to die with signal 8 (floating point 
exceptions). This was caused by the fix to issue 17243.

This is a good thing. We need more breaking changes like this.

Now that the floating point exceptions are properly enabled we 
were able to track down some issues that were being silently 
ignored.
May 08
parent reply Adam Wilson <flyboynw gmail.com> writes:
On 5/8/17 20:33, Brian Schott wrote:
 Recently the EMSI data department upgraded the compiler we use to build
 our data processing code to 2.074. This caused several of the thousands
 of processes to die with signal 8 (floating point exceptions). This was
 caused by the fix to issue 17243.

 This is a good thing. We need more breaking changes like this.

 Now that the floating point exceptions are properly enabled we were able
 to track down some issues that were being silently ignored.
WUT. All I hear on these forums is the abject terror of breaking changes making companies run screaming from D. You mean to say that companies don't actually mind breaking changes if it solves long-standing issues. I'm shocked. SHOCKED I SAY! ;-) Can we PLEASE get more of this? I'm not saying up-end the language, but let's solve some problems. I doubt our corporate users will be very angry. I suspect that most reactions will fall between "minor irritation" and this one. /me looks sideways at shared -- Adam Wilson IRC: LightBender import quiet.dlang.dev;
May 09
next sibling parent David Eckardt <david.eckardt sociomantic.com> writes:
On Tuesday, 9 May 2017 at 12:13:34 UTC, Adam Wilson wrote:
 Can we PLEASE get more of this? I'm not saying up-end the 
 language, but let's solve some problems. I doubt our corporate 
 users will be very angry. I suspect that most reactions will 
 fall between "minor irritation" and this one.

 /me looks sideways at shared
Here is more of this. Writing industry D code I *love* breaking changes that reveal bugs in the code base. A while ago I suggested changing cast(bool) semantics of floating-point types so that assert(x) fails if x is NaN (https://issues.dlang.org/show_bug.cgi?id=13489). It was rejected because it could break existing code and surprise C/C++ programmers (although the point of NaN is to surprise you IMHO). I wonder what the ratio of valid to buggy code is that would break with this change. At least I enjoy it if my buggy code breaks.
May 09
prev sibling parent reply "H. S. Teoh via Digitalmars-d" <digitalmars-d puremagic.com> writes:
On Tue, May 09, 2017 at 02:13:34PM +0200, Adam Wilson via Digitalmars-d wrote:
 On 5/8/17 20:33, Brian Schott wrote:
 Recently the EMSI data department upgraded the compiler we use to
 build our data processing code to 2.074. This caused several of the
 thousands of processes to die with signal 8 (floating point
 exceptions). This was caused by the fix to issue 17243.
 
 This is a good thing. We need more breaking changes like this.
[...]
 WUT.
 
 All I hear on these forums is the abject terror of breaking changes
 making companies run screaming from D. You mean to say that companies
 don't actually mind breaking changes if it solves long-standing
 issues.
 
 I'm shocked. SHOCKED I SAY!
 
 ;-)
 
 Can we PLEASE get more of this? I'm not saying up-end the language,
 but let's solve some problems. I doubt our corporate users will be
 very angry. I suspect that most reactions will fall between "minor
 irritation" and this one.
 
 /me looks sideways at shared
[...] I don't represent any company, but I have to also say that I *appreciate* breaking changes that reveal latent bugs in my code. In fact, I even appreciate breakages that eventually force me to write more readable code! A not-so-recent example: /* Used to work, oh, I forget which version now, but it used to * work: */ MyType* ptr = ...; if (someCondition && ptr) { ... } After upgrading the compiler, I get a warning that using a pointer as a condition is deprecated. At first I was mildly annoyed... but then to make the warning go away, I wrote this instead: /* Look, ma! Self-documenting, readable code! */ MyType* ptr = ...; if (someCondition && ptr !is null) { ... } Much more readable, and makes intent so much clearer. Eventually I was very happy this supposedly "big bad" breaking change was made. I wish Walter & Andrei & gang would introduce this sort of breakages more often. They will both improve the language and impress users whose code we are so afraid of breaking (I'm still not sure why). T -- Stop staring at me like that! It's offens... no, you'll hurt your eyes!
May 09
next sibling parent reply Patrick Schluter <Patrick.Schluter bbox.fr> writes:
On Tuesday, 9 May 2017 at 17:34:48 UTC, H. S. Teoh wrote:
 On Tue, May 09, 2017 at 02:13:34PM +0200, Adam Wilson via 
 Digitalmars-d wrote:
 [...]
[...]
 [...]
[...] I don't represent any company, but I have to also say that I *appreciate* breaking changes that reveal latent bugs in my code. In fact, I even appreciate breakages that eventually force me to write more readable code! A not-so-recent example: [...]
The code breakage annoyance has more to do with 3rd party libraries not very actively maintained than with active codebases imho.
May 09
parent reply Adam Wilson <flyboynw gmail.com> writes:
On 5/9/17 20:23, Patrick Schluter wrote:
 On Tuesday, 9 May 2017 at 17:34:48 UTC, H. S. Teoh wrote:
 On Tue, May 09, 2017 at 02:13:34PM +0200, Adam Wilson via
 Digitalmars-d wrote:
 [...]
[...]
 [...]
[...] I don't represent any company, but I have to also say that I *appreciate* breaking changes that reveal latent bugs in my code. In fact, I even appreciate breakages that eventually force me to write more readable code! A not-so-recent example: [...]
The code breakage annoyance has more to do with 3rd party libraries not very actively maintained than with active codebases imho.
*cough* Umm, I think that's a false pointer. If it's not actively maintained, should you really be relying on it? Where I work, current maintenance is one of the first questions we ask, followed immediately by determining whether or not we are able to maintain it ourselves should it go unmaintained. If you're going to take on maintenance yourself, the library is already missing features and you're responsible for fixing it's existing implementation bugs anyways, might as well do the work of upgrading it while you're at it. This is the point of Open Source, we have the opportunity to take unmaintained code and start maintaining it again. Either way all I hear about is corp users not liking breaking changes. That has been demonstrated as a false concern time and time again. If it's a matter of unmaintained libraries, those libraries probably have bigger problems than breaking compiler changes, fork and upgrade them or write your own. Because those have always been the only two choices you've ever had in practice anyways. Telling the world that we can't make breaking changes to the compiler because it might break an unmaintained library is irrational position and extreme position to take. It will *not* win us hearts and minds. Let's stop hiding behind our misplaced fears over corp-users and unmaintained libraries so that we can start improving D for everyone who is using it today. -- Adam Wilson IRC: LightBender import quiet.dlang.dev;
May 09
parent "H. S. Teoh via Digitalmars-d" <digitalmars-d puremagic.com> writes:
On Wed, May 10, 2017 at 03:19:20AM +0200, Adam Wilson via Digitalmars-d wrote:
[...]
 Either way all I hear about is corp users not liking breaking changes.
 That has been demonstrated as a false concern time and time again.
 
 If it's a matter of unmaintained libraries, those libraries probably
 have bigger problems than breaking compiler changes, fork and upgrade
 them or write your own. Because those have always been the only two
 choices you've ever had in practice anyways. Telling the world that we
 can't make breaking changes to the compiler because it might break an
 unmaintained library is irrational position and extreme position to
 take. It will *not* win us hearts and minds.
 
 Let's stop hiding behind our misplaced fears over corp-users and
 unmaintained libraries so that we can start improving D for everyone
 who is using it today.
[...] +1. Please let's not go down the same path that led C++ to become what it is today: an overly complex language that almost nobody fully understands (and even fewer can write correct code in), yet still suffering under the weight of design mistakes of decades ago. But in spite of all that, it's, oh, backward-compatible! How wonderful, I can still compile my horribly-broken, memory-leaking, pointer-bug-infested excuse for code that I wrote when I was still in college! Isn't that great? Oh wait, it just segfaulted. No biggie, I can fix that easily ... give gimme a minute... uh... um... OK, WHAT has changed since the 90's that I can't do *this* anymore? (5 hours later) I give up, this code is useless. Why does C++ still bother supporting this crappy excuse for code after 2 decades?! T -- Skill without imagination is craftsmanship and gives us many useful objects such as wickerwork picnic baskets. Imagination without skill gives us modern art. -- Tom Stoppard
May 09
prev sibling parent reply =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
On 05/09/2017 10:34 AM, H. S. Teoh via Digitalmars-d wrote:

 I even appreciate breakages that eventually force me to write more
 readable code!  A not-so-recent example:

 	/* Used to work, oh, I forget which version now, but it used to
 	 * work: */
 	MyType* ptr = ...;
 	if (someCondition && ptr) { ... }

 After upgrading the compiler, I get a warning that using a pointer as a
 condition is deprecated.  At first I was mildly annoyed... but then to
 make the warning go away, I wrote this instead:

 	/* Look, ma! Self-documenting, readable code! */
 	MyType* ptr = ...;
 	if (someCondition && ptr !is null) { ... }
Can you show an example please. I don't see this being required by 2.074.0 (compiled with -w -de). Thank you, Ali
May 10
parent reply Jonathan M Davis via Digitalmars-d <digitalmars-d puremagic.com> writes:
On Wednesday, May 10, 2017 05:05:59 Ali «ehreli via Digitalmars-d wrote:
 On 05/09/2017 10:34 AM, H. S. Teoh via Digitalmars-d wrote:
  > I even appreciate breakages that eventually force me to write more
  >
  > readable code!  A not-so-recent example:
  >    /* Used to work, oh, I forget which version now, but it used to
  >
  >     * work: */
  >
  >    MyType* ptr = ...;
  >    if (someCondition && ptr) { ... }
  >
  > After upgrading the compiler, I get a warning that using a pointer as a
  > condition is deprecated.  At first I was mildly annoyed... but then to
  >
  > make the warning go away, I wrote this instead:
  >    /* Look, ma! Self-documenting, readable code! */
  >    MyType* ptr = ...;
  >    if (someCondition && ptr !is null) { ... }

 Can you show an example please. I don't see this being required by
 2.074.0 (compiled with -w -de).
I think that that's the one that Andrei and Vladimir didn't like, because they actually used the conversion to bool correctly in their code a bunch (whereas most everyone else thought that it was too error prone), and the deprecation ended up being removed. - Jonathan M Davis
May 10
next sibling parent reply =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
On 05/10/2017 11:49 AM, Jonathan M Davis via Digitalmars-d wrote:
 On Wednesday, May 10, 2017 05:05:59 Ali Çehreli via Digitalmars-d wrote:
 On 05/09/2017 10:34 AM, H. S. Teoh via Digitalmars-d wrote:
  > After upgrading the compiler, I get a warning that using a 
pointer as a
  > condition is deprecated.
 I think that that's the one that Andrei and Vladimir didn't like, because
 they actually used the conversion to bool correctly in their code a bunch
 (whereas most everyone else thought that it was too error prone), and the
 deprecation ended up being removed.

 - Jonathan M Davis
Bummer for H. S. Teoh I guess... :/ Although I prefer explicit over implicit in most cases, I've never graduated from if(p) and still using it happily. :) Ali
May 10
next sibling parent Jonathan M Davis via Digitalmars-d <digitalmars-d puremagic.com> writes:
On Wednesday, May 10, 2017 12:06:40 Ali «ehreli via Digitalmars-d wrote:
 On 05/10/2017 11:49 AM, Jonathan M Davis via Digitalmars-d wrote:
  > On Wednesday, May 10, 2017 05:05:59 Ali «ehreli via Digitalmars-d 
wrote:
  >> On 05/09/2017 10:34 AM, H. S. Teoh via Digitalmars-d wrote:
  >>  > After upgrading the compiler, I get a warning that using a

 pointer as a

  >>  > condition is deprecated.
  >
  > I think that that's the one that Andrei and Vladimir didn't like,
  > because
  > they actually used the conversion to bool correctly in their code a
  > bunch
  > (whereas most everyone else thought that it was too error prone), and
  > the
  > deprecation ended up being removed.
  >
  > - Jonathan M Davis

 Bummer for H. S. Teoh I guess... :/

 Although I prefer explicit over implicit in most cases, I've never
 graduated from if(p) and still using it happily. :)
The big problems is dynamic arrays, not pointers, which was what I thought that H. S. Teoh was talking about (apparently, I read over what he said too quickly). I don't know about deprecations with pointers and if statements, and I use pointers sparingly enough in D that I don't know how long it would be before I'd notice if it _were_ deprecated. But with dynamic arrays, it checks for null, not for empty, and a bunch of folks tend to assume that it checks for empty. Using dynamic arrays directly in if conditions is what had been deprecated (on the theory that it was too error-prone) and what Andrei and Vladimir were unhappy about. - Jonathan M Davis
May 10
prev sibling next sibling parent reply deadalnix <deadalnix gmail.com> writes:
On Wednesday, 10 May 2017 at 19:06:40 UTC, Ali Çehreli wrote:
 Bummer for H. S. Teoh I guess... :/

 Although I prefer explicit over implicit in most cases, I've 
 never graduated from if(p) and still using it happily. :)

 Ali
All bool conversions in D are value based, not identity based. Not only this is error prone, this is inconsistent.
May 11
parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
On 5/11/17 5:37 AM, deadalnix wrote:
 On Wednesday, 10 May 2017 at 19:06:40 UTC, Ali Çehreli wrote:
 Bummer for H. S. Teoh I guess... :/

 Although I prefer explicit over implicit in most cases, I've never
 graduated from if(p) and still using it happily. :)

 Ali
All bool conversions in D are value based, not identity based. Not only this is error prone, this is inconsistent.
What does "value based" and "identity based" mean? bool conversions vary widely and allow a lot of flexibility (at least for structs): if(arr) -> same as if(arr.ptr) if(someInt) -> same as if(someInt != 0) if(someObject) -> if(someObject !is null && someObject.invariant) if(someStruct) -> if(someStruct.opCast!(bool)) -Steve
May 11
parent reply deadalnix <deadalnix gmail.com> writes:
On Thursday, 11 May 2017 at 12:26:11 UTC, Steven Schveighoffer 
wrote:
 if(arr) -> same as if(arr.ptr)
Nope. It is: if(arr) -> same as if(((cast(size_t) arr.ptr) | arr.length) != 0) Should we conclude from the fact that absolutely nobody gets it right in this very forum that nobody will get it right outside ? I'll let you judge.
May 11
next sibling parent Steven Schveighoffer <schveiguy yahoo.com> writes:
On 5/11/17 7:12 PM, deadalnix wrote:
 On Thursday, 11 May 2017 at 12:26:11 UTC, Steven Schveighoffer wrote:
 if(arr) -> same as if(arr.ptr)
Nope. It is: if(arr) -> same as if(((cast(size_t) arr.ptr) | arr.length) != 0) Should we conclude from the fact that absolutely nobody gets it right in this very forum that nobody will get it right outside ? I'll let you judge.
Yep, you are right. It's checking the length too. Although in practice, almost never do you have a null pointer array with non-zero length. Just for your amusement, I wrote the test this way :) Stevens-MacBook-Pro:testd steves$ cat testifarrptr.d void main() { char[] x = null; x = x.ptr[0 .. 1]; if(x) { import std.stdio; writeln("ok, deadalnix was right"); } } Stevens-MacBook-Pro:testd steves$ dmd -run testifarrptr.d ok, deadalnix was right -Steve
May 11
prev sibling parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
On 5/11/17 7:12 PM, deadalnix wrote:
 On Thursday, 11 May 2017 at 12:26:11 UTC, Steven Schveighoffer wrote:
 if(arr) -> same as if(arr.ptr)
Nope. It is: if(arr) -> same as if(((cast(size_t) arr.ptr) | arr.length) != 0) Should we conclude from the fact that absolutely nobody gets it right in this very forum that nobody will get it right outside ? I'll let you judge.
But this still doesn't mean that *all* bool conversions are value based. In at least the struct and class cases, more than just the bits are checked. -Steve
May 11
parent reply "H. S. Teoh via Digitalmars-d" <digitalmars-d puremagic.com> writes:
On Thu, May 11, 2017 at 07:46:24PM -0400, Steven Schveighoffer via
Digitalmars-d wrote:
[...]
 But this still doesn't mean that *all* bool conversions are value
 based. In at least the struct and class cases, more than just the bits
 are checked.
[...] Wait, what? You can use a *struct* as a bool condition?! I tried this: import std.stdio; struct S {} void main() { S s; if (s) { writeln("WAT"); } } But the compiler (rightly) said: test.d(5): Error: expression s of type S does not have a boolean value Or were you talking about structs that define opCast!bool? (In which case it's certainly intentional and doesn't pose a problem.) I can see classes being usable in conditions, though, since they're essentially pointers hiding behind an abstraction. Still, it doesn't quite sit right with me. For example: class C { } class D { bool opCast(T : bool)() { return false; } } void main() { C c; D d = new D; if (!c) { ... } // OK, expected semantics if (!d) { ... } // *** What happens here? } Whereas had the last two lines been written: if (c is null) { ... } if (d is null) { ... } the intent would be much clearer. (And of course, d would be usable without "is null" if you actually intended to invoke opCast!bool.) T -- In a world without fences, who needs Windows and Gates? -- Christian Surchi
May 11
parent Steven Schveighoffer <schveiguy yahoo.com> writes:
On 5/11/17 7:52 PM, H. S. Teoh via Digitalmars-d wrote:
 On Thu, May 11, 2017 at 07:46:24PM -0400, Steven Schveighoffer via
Digitalmars-d wrote:
 [...]
 But this still doesn't mean that *all* bool conversions are value
 based. In at least the struct and class cases, more than just the bits
 are checked.
[...] Wait, what? You can use a *struct* as a bool condition?! I tried this: import std.stdio; struct S {} void main() { S s; if (s) { writeln("WAT"); } } But the compiler (rightly) said: test.d(5): Error: expression s of type S does not have a boolean value Or were you talking about structs that define opCast!bool? (In which case it's certainly intentional and doesn't pose a problem.)
Yes, I was talking about that.
 I can see classes being usable in conditions, though, since they're
 essentially pointers hiding behind an abstraction. Still, it doesn't
 quite sit right with me. For example:

 	class C { }
 	class D { bool opCast(T : bool)() { return false; } }
 	void main() {
 		C c;
 		D d = new D;

 		if (!c) { ... }		// OK, expected semantics
 		if (!d) { ... }		// *** What happens here?
 	}
I have totally misremembered the thing of classes and invariants. So my statement on that is wrong. It's when you *assert* a class instance that the invariant is checked, not when used in an if condition. Testing out your question, the opCast to bool doesn't apply for classes. Just the reference is checked against null. You have to write cast(bool)d to trigger the opCast. So classes are just like pointers. Really only structs offer some semblance of control for use in if statements. -Steve
May 12
prev sibling parent Patrick Schluter <Patrick.Schluter bbox.fr> writes:
On Wednesday, 10 May 2017 at 19:06:40 UTC, Ali Çehreli wrote:
 On 05/10/2017 11:49 AM, Jonathan M Davis via Digitalmars-d 
 wrote:
 On Wednesday, May 10, 2017 05:05:59 Ali Çehreli via
Digitalmars-d wrote:
 On 05/09/2017 10:34 AM, H. S. Teoh via Digitalmars-d wrote:
  > After upgrading the compiler, I get a warning that using
a pointer as a
  > condition is deprecated.
 I think that that's the one that Andrei and Vladimir didn't
like, because
 they actually used the conversion to bool correctly in their
code a bunch
 (whereas most everyone else thought that it was too error
prone), and the
 deprecation ended up being removed.

 - Jonathan M Davis
Bummer for H. S. Teoh I guess... :/ Although I prefer explicit over implicit in most cases, I've never graduated from if(p) and still using it happily. :)
Yes, me too (in C). It is conceptually imho ok to use it that way as a pointer does have a boolean semantic, either it is a valid pointer or it is not. The value of the pointer itself is only in special cases relevant (cases in which they have to be converted to an integral type anyway) and is in any case extremely machine dependent. One can even make the case that checking "ptr !is null" or in C "ptr != 0" is inconsistent as it is the only operation where the value of a pointer is used, which is, at least for C a source of confusion. The 0 value in a pointer context will not necessarily compile to a 0 value in the generated assembly. Some machines have null ptrs that are not represented by 0 bits integral values and the C standard has to take these (granted obsolete) into account.
May 11
prev sibling parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
On 5/10/17 2:49 PM, Jonathan M Davis via Digitalmars-d wrote:
 On Wednesday, May 10, 2017 05:05:59 Ali «ehreli via Digitalmars-d wrote:
 On 05/09/2017 10:34 AM, H. S. Teoh via Digitalmars-d wrote:
  > I even appreciate breakages that eventually force me to write more
  >
  > readable code!  A not-so-recent example:
  >    /* Used to work, oh, I forget which version now, but it used to
  >
  >     * work: */
  >
  >    MyType* ptr = ...;
  >    if (someCondition && ptr) { ... }
  >
  > After upgrading the compiler, I get a warning that using a pointer as a
  > condition is deprecated.  At first I was mildly annoyed... but then to
  >
  > make the warning go away, I wrote this instead:
  >    /* Look, ma! Self-documenting, readable code! */
  >    MyType* ptr = ...;
  >    if (someCondition && ptr !is null) { ... }

 Can you show an example please. I don't see this being required by
 2.074.0 (compiled with -w -de).
I think that that's the one that Andrei and Vladimir didn't like, because they actually used the conversion to bool correctly in their code a bunch (whereas most everyone else thought that it was too error prone), and the deprecation ended up being removed.
I think that was the if(array) fiasco. I don't ever remember if(ptr) being deprecated. In fact, I'd go as far as saying that maybe H.S. Teoh misremembers the array thing as pointers. The biggest reason is that a huge useful pattern with this is: if(auto x = key in someAA) { // use *x without more hash lookup costs. } I can't imagine anyone attempted to force this to break without a loud backlash. I think if(ptr) is mostly universally understood to mean the pointer is not null. -Steve
May 11
next sibling parent deadalnix <deadalnix gmail.com> writes:
On Thursday, 11 May 2017 at 12:21:46 UTC, Steven Schveighoffer 
wrote:
 I can't imagine anyone attempted to force this to break without 
 a loud backlash. I think if(ptr) is mostly universally 
 understood to mean the pointer is not null.

 -Steve
It is not a problem for pointer because for pointer identity and equality are the same thing. It isn't for slices.
May 11
prev sibling parent "H. S. Teoh via Digitalmars-d" <digitalmars-d puremagic.com> writes:
On Thu, May 11, 2017 at 08:21:46AM -0400, Steven Schveighoffer via
Digitalmars-d wrote:
[...]
 I don't ever remember if(ptr) being deprecated. In fact, I'd go as far
 as saying that maybe H.S. Teoh misremembers the array thing as
 pointers.
 
 The biggest reason is that a huge useful pattern with this is:
 
 if(auto x = key in someAA)
 {
    // use *x without more hash lookup costs.
 }
 
 I can't imagine anyone attempted to force this to break without a loud
 backlash. I think if(ptr) is mostly universally understood to mean the
 pointer is not null.
[...] Since the accuracy of my memory was questioned, I went back to look at the code in question, and indeed I did misremember it, but it was not with arrays, it was with casting pointers to bool. And it was in a while-condition, not an if-condition. Here's a simplified version of the original code: struct Op {...} Op* getOp(...) { ... } ... Op* op; while (!input.empty && cast(bool)(op = getOp(...))) { ... } The cast(bool) used to be accepted up to a certain version (it was in the code from when I first wrote it around 2012), then around 2013 it became a compile error, which forced me to rewrite it as: struct Op {...} Op* getOp(...) { ... } ... Op* op; while (!input.empty && (op = getOp(...)) !is null) { ... } which is much more readable and documents intent more clearly. I originally wrote the cast(bool) because without it the compiler rejects using assignment in an while-condition. I suppose the reasoning is that it's too easy to mistakenly write `while (a=b)` instead of `while (a==b)`. In modern C compilers, an extra set of parentheses usually silenced the compiler warning about a possible typo of ==, but in D even with parentheses the compiler would reject it. So all things considering, this little anecdote represents the following progression in readability (the first 2 steps are hypothetical, since they're only permitted in C): while (a = b) ... // in C, error-prone, could be typo while ((a = b)) ... // still in C, marginally better while (cast(bool)(a = b)) // early D, the conversion is now explicit while ((a = b) !is null) // present-day D, finally intent is clear T -- By understanding a machine-oriented language, the programmer will tend to use a much more efficient method; it is much closer to reality. -- D. Knuth
May 11