Nick Sabalausky wrote: >> Yes, this approach is what I was getting at. In fact, I would (and >> already have in the past) argue that this is *better* than the "holy >> grail" approach, because because it's based on very simple and easy to >> remember rules. Conversely, the "holy grail" approach leads to >> difficult-to-predict cases of small, seemingly-innocent changes in one >> place causing some other code to suddenly switch back and forth between >> "compiles" and "doesn't compile". Take this modified version of your >> example: >> >> ------------ >> // Imagine foo resides in a completely different package >> int foo() { return 5; } >> >> int i; >> for(int j = foo(); j > 3; j--) i = j; >> auto k = i; // Compiles at the moment... >> ------------ >> >> Now make a perfectly acceptable-looking change to foo: >> ------------ >> int foo() { return 2; } >> ------------ >> >> And all of a sudden non-local code starts flip-flopping between >> "compiles" and "doesn't compile".
Better than a flipflop between "runs correctly" and "runs incorrectly", wouldn't you agree? But of course, you're arguing on the other end of the spectrum. Read on. >> Additionally, even the "holy grail" approach still has to reduce itself >> to being overly conservative in certain cases anyway: >> ------------ >> int foo() >> { >> auto rnd = new RandomGenerator(); >> rnd.seed(systemClock); >> return rnd.fromRange(1,10); >> } >> ------------ I wouldn't call the "holy grail" overly conservative in this instance. The post-condition of 'foo' would simply be (1 <= returnValue <= 10). With no more information than that, the compiler would have to give an error, since 'foo' *may return a value* that results in an uninitialized read of 'i'. That's how it should work. No errors if and only if there is no possible execution path that results in failure, be it uninitialized-read failure, null-dereference failure or divide-by-zero failure. >> So, we only have two initial choices: >> - Overly conservative (C#-style or "holy grail") >> - Overly permissive (current D approach) I tend to agree with BCS that the programmer should have the last say, unless the compiler can absolutely prove that (s)he is wrong. Given the choice between overly conservative and overly permissive, I would pick overly permissive. But the beauty of the holy grail is that it's neither. > Additionally, in the C# approach (and this is speaking from personal > experience), anytime you do come across a provably-correct case that the > compiler rejects, not only is it always obvious to see why the compiler > rejected it, but it's also trivially easy to fix. So in practice, it's > really not much of a "baby with the bathwater" situation at all. But what would the fix be in the case of our example? Surely you're not suggesting initializing 'i' to 0? Then we'd be back in the old situation where we might get unexpected runtime behavior if we were wrong about 'foo'. An acceptable solution would be: int i; assert(foo() > 3); for(int j = foo(); j > 3; j--) i = j; auto k = i; // Compiles at the moment... Would C# swallow that? -- Michiel Helvensteijn