September 27, 2009
Andrei Alexandrescu:

> The problem is you keep on insisting on one case "I have a non-null reference that I don't have an initializer for, but the compiler forces me to find one, so I'll just throw a crappy value in." This focus on one situation comes straight with your admitted bad habit of defining variables in one place and initializing in another.

Thank you Andrei for your good efforts in trying to add some light on this topic. I think we are converging :-)

But I think you have to deal with the example shown by Jeremie Pelletier too, this was my answer: http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D&article_id=96834

(What I have written in the last line is confused. I meant that the type system doesn't allow you to read or access an object before it's initialized. This looks like flow analysis, but there are ways to simplify/constraint the situation enough, for example with that enforce scope block).

Bye,
bearophile
September 27, 2009
On Sat, Sep 26, 2009 at 7:21 PM, Jeremie Pelletier <jeremiep@gmail.com> wrote:
> That's exactly the point with nonnull references, they turn access violations or segfaults into undefined behavior, or worse into generic behavior that's much harder to track to its source.
>
> I think nonnull references are a nice concept for languages that have a higher level than D. If I expect references to never be null I just don't check for null before using them, and let the code crash which gives me a nice crash window with a backtrace in my runtime.

You're missing the point. You wouldn't have "undefined behavior at runtime" with nonnull references because there would be NO POINT in having nonnull references without ALSO having nullable references.

Could your reference be null? Use a nullable reference.

Is your reference never supposed to be null? Use a nonnull reference.

End of problem. You do not create "null objects" and store them in a nonnull reference which you then check at runtime. You use a nullable reference which the language *forces* you to check before use.
September 27, 2009
On Sat, Sep 26, 2009 at 10:41 PM, Walter Bright <newshound1@digitalmars.com> wrote:

>> And what about the people who AREN'T complete idiots, who maybe sometimes just accidentally trip and would quite welcome a safety rail there?
>
> Null pointer seg faults *are* a safety rail. They keep an errant program from causing further damage.

If you haven't crawled out from under your rock in the last twenty years, I'd like to point out that the accepted definition of safety and program correctness has changed since then.
September 27, 2009
Yigal Chripun wrote:
> 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.

This is something for the runtime or the debugger to deal with. My runtime converts access violations on windows or segfaults on linux into exception objects, which unwind all the way down to main where it catches into the unhandled exception handler (or crash handler) and I get a neat popup with a "hello, your program crashed at this point, here is a backtrace with resolved symbols and filenames along with current registers and loaded modules, would you like a cup of coffee while you solve the problem?". I sent that crash handler to D.announce last week too.

The compiler won't be able to enforce *every* nonnull reference and segfaults are bound to happen, especially with casting. While it may prevent most of them, any good programmer would too, I don't remember the last time I had a segfault on a null reference actually.

I can see what the point is with nonnull references, but I can also see its not a bulletproof solution. ie "Object foo = cast(Object)null;" would easily bypass the nonnull enforcement, resulting in a segfault the system is trying to avoid.

What about function parameters, a lot of parameters are optional references, which are tested and then used into functions whose parameters aren't optional. It would result in a lot of casts, something that could easily confuse people and easily generate segfaults.

Alls I'm saying is, nonnull references would just take the issue from one place to another. Like Walter said, you can put a gas mask to ignore the room full of toxic gas, but that doesn't solve the gas problem in itself, you're just denyinng it exists. Then someday you forget about it, remove the mask, and suffocate.

Jeremie
September 27, 2009
On Sat, Sep 26, 2009 at 10:59 PM, Jeremie Pelletier <jeremiep@gmail.com> wrote:

> The compiler won't be able to enforce *every* nonnull reference and segfaults are bound to happen, especially with casting. While it may prevent most of them, any good programmer would too, I don't remember the last time I had a segfault on a null reference actually.
>
> I can see what the point is with nonnull references, but I can also see its not a bulletproof solution. ie "Object foo = cast(Object)null;" would easily bypass the nonnull enforcement, resulting in a segfault the system is trying to avoid.
>
> What about function parameters, a lot of parameters are optional references, which are tested and then used into functions whose parameters aren't optional. It would result in a lot of casts, something that could easily confuse people and easily generate segfaults.

You haven't read my reply to your post yet, have you.

Nullable.



References.



Exist.



Too.
September 27, 2009
Jarrett Billingsley wrote:
> On Sat, Sep 26, 2009 at 7:21 PM, Jeremie Pelletier <jeremiep@gmail.com> wrote:
>> That's exactly the point with nonnull references, they turn access
>> violations or segfaults into undefined behavior, or worse into generic
>> behavior that's much harder to track to its source.
>>
>> I think nonnull references are a nice concept for languages that have a
>> higher level than D. If I expect references to never be null I just don't
>> check for null before using them, and let the code crash which gives me a
>> nice crash window with a backtrace in my runtime.
> 
> You're missing the point. You wouldn't have "undefined behavior at
> runtime" with nonnull references because there would be NO POINT in
> having nonnull references without ALSO having nullable references.
> 
> Could your reference be null? Use a nullable reference.
> 
> Is your reference never supposed to be null? Use a nonnull reference.
> 
> End of problem. You do not create "null objects" and store them in a
> nonnull reference which you then check at runtime. You use a nullable
> reference which the language *forces* you to check before use.

I don't want the language to force me to check nullable references before using them, that just takes away a lot of optimization cases.

You could just use the casting system to sneak null into a nonnull reference and bam, undefined behavior. And you could have nullables which are always nonnull at some point in time within a scope but your only way out of the compiler errors about using a nullable without first testing it for nullity is to use excessive casting.
September 27, 2009
Walter Bright:

> The only time I've had a problem finding where a null came from (because they tend to fail very close to their initialization point) is when the null was caused by another memory corruption problem. Non-nullable references won't mitigate that.

There are some ways to reduce the number/probability of memory corruptions too in a C-like language. Memory regions, region analysis, etc. We can discuss about this too, but this is another topic.

Bye,
bearophile
September 27, 2009
On Sat, Sep 26, 2009 at 11:06 PM, Jeremie Pelletier <jeremiep@gmail.com> wrote:
>
> I don't want the language to force me to check nullable references before using them, that just takes away a lot of optimization cases.

You don't design tight loops that dereference pointers with the intention that those pointers will ever be null. Those loops always expect nonnull pointers, and therefore you'd use a nonnull reference.

The number of null references in your program are far less than you'd think. For those that really could be legitimately null (like an optional callback or something), you have to check for null at runtime anyway. Most of your code wouldn't really change. You'd instead just get more errors at compile time for things that are obviously illegal or just very potentially dangerous.

> You could just use the casting system to sneak null into a nonnull reference and bam, undefined behavior.

No, you couldn't. That would be a pretty shitty nonnull reference type if the compiler let you put null in it.

> And you could have nullables which are always
> nonnull at some point in time within a scope but your only way out of the
> compiler errors about using a nullable without first testing it for nullity
> is to use excessive casting.

The argument of verbosity that comes up with nonnull references holds some weight, but it's far more a matter of designing your code not to do something like that.
September 27, 2009
Ary Borenszweig wrote:
> Walter Bright wrote:
>> Daniel Keep wrote:
>>> "But the user will just assign to something useless to get around that!"
>>>
>>> You mean like how everyone wraps every call in try{...}catch(Exception
>>> e){} to shut the damn exceptions up?
>>
>> They do just that in Java because of the checked-exceptions thing. I have a reference to Bruce Eckel's essay on it somewhere in this thread. The observation in the article was it wasn't just moron idiot programmers doing this. It was the guru programmers doing it, all the while knowing it was the wrong thing to do. The end result was the feature actively created the very problems it was designed to prevent.
>>
>>
>>> Or uses pointer arithmetic and
>>> casts to get at those pesky private members?
>>
>> That's entirely different, because privacy is selected by the programmer, not the language. I don't have any issue with a user-defined type that is non-nullable (Andrei has designed a type constructor for that).
>>
>>
>>> If someone is actively trying to break the type system, it's their
>>> goddamn fault!  Honestly, I don't care about the hacks they employ to
>>> defeat the system because they're going to go around blindly shooting
>>> themselves in the foot no matter what they do.
>>
>> True, but it's still not a good idea to design a language feature that winds up, in reality, encouraging bad programming practice. It encourages bad practice in a way that is really, really hard to detect in a code review.
>>
>> I like programming mistakes to be obvious, not subtle. There's nothing subtle about a null pointer exception. There's plenty subtle about the wrong default value.
>>
>>
>>> And what about the people who AREN'T complete idiots, who maybe
>>> sometimes just accidentally trip and would quite welcome a safety rail
>>> there?
>>
>> Null pointer seg faults *are* a safety rail. They keep an errant program from causing further damage.
> 
> Null pointer seg faults *not being able to happen* are much more safe. :)

There is no such thing as "not being able to happen" :)

Object thisCannotPossiblyBeNullInAnyWayWhatsoever = cast(Object)null;

I seem to be the only one who sees Walter's side of things in this thread :o)

For nonnulls to *really* be enforcable you'd have to get rid of the cast system entirely.
September 27, 2009
On Sat, Sep 26, 2009 at 11:23 PM, Jeremie Pelletier <jeremiep@gmail.com> wrote:

> There is no such thing as "not being able to happen" :)
>
> Object thisCannotPossiblyBeNullInAnyWayWhatsoever = cast(Object)null;
>
> I seem to be the only one who sees Walter's side of things in this thread :o)

Why the hell would the compiler allow that to begin with? Why bother implementing nonnull references only to allow the entire system to be broken?