September 27, 2009
On 27/09/2009 00:59, Jeremie Pelletier wrote:
> Jarrett Billingsley wrote:
>> On Sat, Sep 26, 2009 at 5:29 PM, Jeremie Pelletier
>> <jeremiep@gmail.com> wrote:
>>
>>> I actually side with Walter here. I much prefer my programs to crash on
>>> using a null reference and fix the issue than add runtime overhead
>>> that does
>>> the same thing. In most cases a simple backtrace is enough to
>>> pinpoint the
>>> location of the bug.
>>
>> There is NO RUNTIME OVERHEAD in implementing nonnull reference types.
>> None. It's handled entirely by the type system. Can we please move
>> past this?
>>
>>> Null references are useful to implement optional arguments without any
>>> overhead by an Optional!T wrapper. If you disallow null references what
>>> would "Object foo;" initialize to then?
>>
>> It wouldn't. The compiler wouldn't allow it. It would force you to
>> initialize it. That is the entire point of nonnull references.
>
> How would you do this then?
>
> void foo(int a) {
> Object foo;
> if(a == 1) foo = new Object1;
> else if(a == 2) foo = Object2;
> else foo = Object3;
> foo.doSomething();
> }
>
> The compiler would just die on the first line of the method where foo is
> null.
>
> What about "int a;" should this throw an error too? Or "float f;".
>
> What about standard pointers? I can think of so many algorithms who rely
> on pointers possibly being null.
>
> Maybe this could be a case to add in SafeD but leave out in standard D.
> I wouldn't want a nonnull reference type, I use nullables just too often.

with current D syntax this can be implemented as:

void foo(int a) {
  Object foo = (a == 1) ? new Object1
             : (a == 2) ? Object2
             : Object3;
  foo.doSomething();
}

The above agrees also with what Denis said about possible uninitialized variable bugs.

in D "if" is the same as in C - a procedural statement.
I personally think that it should be an expression like in FP languages which is safer.

to reinforce what others have said already:
1) non-null references *by default* does not affect nullable references in any way and does not add any overhead. The idea is to make the *default* the *safer* option which is one of the primary goals of this language.
2) there is no default value for non-nullable references. you must initialize it to a correct, logical value *always*. If you resort to some "default" value you are doing something wrong.

btw, C++ references implement this idea already. functions that return a reference will throw an exception on error (Walter's canary) while the same function that returns a pointer will usually just return null on error.

segfaults are *NOT* a good mechanism to handle errors. An exception trace gives you a whole lot more information about what went wrong and where compared to a segfault.

September 27, 2009
Sat, 26 Sep 2009 15:49:06 -0700, Walter Bright thusly wrote:

> I used to work at Boeing designing critical flight systems. Absolutely the WRONG failure mode is to pretend nothing went wrong and happily return default values and show lovely green lights on the instrument panel.

Basically if there is only one way the system can operate correctly, your approach is to catch errors on runtime (segfaults) until a later iteration of the program development turns out to work correctly or well enough. Meanwhile there are several buggy revisions of the program in the development process.

The idea behind non-nullable types and other contracts is to catch these errors on compile time. Sure, the code is a bit harder to write, but it is safe and never segfaults. The idea is to minimize the amount of runtime errors of all sorts. That's also how other features of statically typed languages work.
September 27, 2009
Sun, 27 Sep 2009 02:04:06 +0200, Yigal Chripun thusly wrote:

> segfaults are *NOT* a good mechanism to handle errors. An exception trace gives you a whole lot more information about what went wrong and where compared to a segfault.

Indeed, especially since in the case of D half of the userbase has a broken linker (optlink) and the other half has a broken debugger (gdb). I much rather write non-segfaulting applications in a language without debugger than buggy crap and debug it with the world's best debugger.
September 27, 2009
Sat, 26 Sep 2009 18:38:56 -0500, Andrei Alexandrescu thusly wrote:

> Your code in
> Phobos reflects that perspective. In the RegExp class, for example, you
> very often define a variable at the top of a long function and
> initialize it halfway through it. I trivially replaced such code with
> the correct code that defines symbols just where they're needed.

Maybe Walter has not yet transitioned from the good olde Pascal/C style programming to the C++/D/Java style?
September 27, 2009
On 27/09/2009 00:51, Walter Bright wrote:
> grauzone wrote:
>> Walter Bright wrote:
>>> It is exactly analogous to a null pointer exception. And it's darned
>>> useful.
>>
>> On Linux, it just generates a segfault. And then you have no idea
>> where the program went wrong. dmd outputting incorrect debugging
>> information (so you have troubles using gdb or even addr2line) doesn't
>> really help here.
>
> Then the problem is incorrect dwarf output, not null pointers.
>
>
>> Not so useful.
>
> It's *still* far more useful than generating corrupt output and
> pretending all is ok.

An exception trace is *far* better than a segfault and that does not require null values.

what's better?
a)
 auto a = new Class; // returns null (out of memory)
 a.foo = 5; // segfault since a is null

OR
b)
 auto a = new Class; // throws an out of memory exception
 a.foo = 5; // doesn't even reach here

no one here argues for option c where a holds garbage and the program generates corrupt output.

September 27, 2009
Andrei Alexandrescu wrote:
> Walter Bright wrote:
>> Even forcing an explicit initializer doesn't actually solve the problem - my experience with such features is programmers simply insert any old value to get the code to pass the compiler, even programmers who know it's a bad idea do it anyway.
> 
> I think you're starting to be wrong at the point where you don't realize that many bugs come from references that people have forgotten to initialize. Once you acknowledge those, you will start to realize that a reference that must compulsively be initialized is valuable.

The problem is it's worse to force people to provide an initializer. Most of the time that will work out ok, but the one silent bad value producing silent bad output overbalances all of it. Null pointer dereferences do not produce bad output that can be overlooked.

It isn't a theoretical problem with providing bad initializers just to shut the compiler up. I have seen it in the wild every time some manager required that code compile without warnings and the compiler warned about no initializer.

I'm very much a fan of increasing D's ability to detect and head off common mistakes, but it's really easy to tip into seducing programmers into writing bad code in order to avoid an overly nagging compiler.

There's the other problem of how to represent an "empty" value. You have to create a special object that then you have to either test for explicitly, or that has member functions that throw. You're no better off with that, and arguably worse off.


> You think from another perspective: you strongly believe that *most* of the time you can't or shouldn't initialize a reference. Your code in Phobos reflects that perspective. In the RegExp class, for example, you very often define a variable at the top of a long function and initialize it halfway through it. I trivially replaced such code with the correct code that defines symbols just where they're needed.

That style doesn't reflect anything more than my old C habits which require declarations before any statements. I know it's bad style and do it less and less over time.
September 27, 2009
language_fan wrote:
> Maybe Walter has not yet transitioned from the good olde Pascal/C style programming to the C++/D/Java style?

Heh, there's still a Fortran influence in my code <g>.
September 27, 2009
Jason House wrote:
> Walter Bright Wrote:
> 
>> Denis Koroskin wrote:
>>> On Sat, 26 Sep 2009 22:30:58 +0400, Walter Bright <newshound1@digitalmars.com> wrote:
>>>> D has borrowed ideas from many different languages. The trick
>>>> is to take the good stuff and avoid their mistakes <g>.
>>> 
>>> How about this one:
>>> 
>> http://sadekdrobi.com/2008/12/22/null-references-the-billion-dollar-mistake/
>> 
>> 
>>> 
>>> 
>>> :)
>> 
>> I think he's wrong.
>> 
>> Getting rid of null references is like solving the problem of dead
>>  canaries in the coal mines by replacing them with stuffed toys.
>> 
>> It all depends on what you prefer a program to do when it
>> encounters a program bug:
> 
> What do you define as a bug?

The program doing something it was not deliberately programmed to do.
September 27, 2009
language_fan wrote:
> The idea behind non-nullable types and other contracts is to catch these errors on compile time. Sure, the code is a bit harder to write, but it is safe and never segfaults. The idea is to minimize the amount of runtime errors of all sorts. That's also how other features of statically typed languages work.


I certainly agree that catching errors at compile time is preferable by far. Where I disagree is the notion that non-nullable types achieve this. I've argued extensively here that they hide errors, not fix them.

Also, by "safe" I presume you mean "memory safe" which means free of memory corruption. Null pointer exceptions are memory safe. A null pointer could be caused by memory corruption, but it cannot *cause* memory corruption.
September 27, 2009
Walter Bright wrote:
> Andrei Alexandrescu wrote:
>> Walter Bright wrote:
>>> Even forcing an explicit initializer doesn't actually solve the problem - my experience with such features is programmers simply insert any old value to get the code to pass the compiler, even programmers who know it's a bad idea do it anyway.
>>
>> I think you're starting to be wrong at the point where you don't realize that many bugs come from references that people have forgotten to initialize. Once you acknowledge those, you will start to realize that a reference that must compulsively be initialized is valuable.
> 
> The problem is it's worse to force people to provide an initializer. 

You're not forcing. You just change the default. Really, it's *exactly* the same deal as with = void that you're so happy about.

Andrei