Null references redux

Adam Burton adz21c at googlemail.com
Mon Sep 28 18:28:55 PDT 2009


I don't know if what I am about to rant about has already been discussed and 
I haven't noticed, but sometimes I feel like sticking my opinions in and 
this seems to be one of them times :-) so bare with me and we'll see if I am 
a crazy man blabbing on about crap or not :-).

language_fan wrote:

> Mon, 28 Sep 2009 15:35:07 -0400, Jesse Phillips thusly wrote:
> 
>> language_fan Wrote:
>> 
>>> > Now if you really want to throw some sticks into the spokes, you
>>> > would say that if the program crashes due to a null pointer, it is
>>> > still likely that the programmer will just initialize/set the value
>>> > to a "default" that still isn't valid just to get the program to
>>> > continue to run.
>>> 
>>> Why should it crash in the first place? I hate crashes. You liek them?
>>> I can prove by structural induction that you do not like them when you
>>> can avoid crashes with static checking.
>> 
>> No one likes programs that crash, doesn't that mean it is an incorrect
>> behavior though?
>> 
>>> Have you ever used functional languages? When you develop in Haskell or
>>> SML, how often you feel there is a good change something will be
>>> initialized to the wrong value? Can you show some statistics that show
>>> how unsafe this practice is?
>> 
>> So isn't that the question? Does/can "default" (by human or machine)
>> initialization create an incorrect state?
Yes but this is possible now anyway. Consider

Foo obj;  // Machine default of null right?
obj.bar(); // Null pointer exception due to null being bad state for the app

Now steps in moron programmer, who would put garbage data into non-nullable 
vars to init them, to fix the issue

Foo obj = new Foo("bleh");  // Fix to avoid null pointer exception (and yes 
i have seen people do this)
obj.bar(); // Logic error but the application soldiers on.
>> If it does, do we continue to
>> work as if nothing was wrong or crash?
Depends on the applications specification/purpose/design? See in a few lines 
what I mean but I don't see how this is pertinent to the discussion. [1]
>> I don't know how often the
>> initialization would be incorrect, but I don't think Walter is concerned
>> with it's frequency, but that it is possible.
Not sure what your getting at, but with my moron programmer example I have 
shown its possible to insert garbage into classes, maybe we should drop them 
too? Also the default machine implementation seemed to screw up too. I think 
there's a point where you have to trust the human factor to do its job 
correctly. If the feature was so ridiculously complex (like depending on 
planetary alignment) that it forced the programmer into stupid practices 
then fair enough, even if its likely most will get it right then that's a 
problem with the feature not the programmer (although I would personally say 
this isn't the case, pending I have understood the feature correctly :-P) 
... if that makes sense (so any technical issues, e.g. I believe someone 
mentioned enforcing it in structs allocated with malloc, are good points 
that I am just not technical enough to comment on, consider me the casual 
hobby reader who has an interest, but not a good background, in systems 
languages). However I think the previous discussions as I remember them seem 
to assert the programmer is an idiot who will initialize with crap, which I 
think is just out of the languages control.
> ...
> 
> It really depends on your subjective opinion whether you want a program
> to segfault or spot a set of errors statically, and have illegally
> behaving non-crashing programs. I say FFFFFFFFFFUUUUUUUUUUU every time I
> experience a segfault. My hobby programs at home are not that critical,
> and at work the critical code is *proven* to be correct so no need to
> worry there.
[1] I think the above touches on an important point when it comes to whether 
it should crash or continue, without a more in depth knowledge its hard to 
say. Some applications it may be possible to crash a process within itself 
(so just throw exception) and return the application to a reasonable state 
that it may continue (like crashing back to the applications main menu and 
letting you start again). Others apps you may want to kill there and then 
(but die gracefully, so rollback transactions etc) before they do more harm.

Regardless the above 2 arguments of crashing vs continuing and the 
incompetence of some developers seems to have no baring on non-nullable. 
Ignoring the fact a function with all non-nullable variables could still 
crash with a non nullpointerexecption, it seems to me if anything non-
nullables just make the application crash earlier when it receives a null 
where not expected. Consider below implemented "normally".

void FuncOne(Foo foo)
{
   ....
   foo = null;   // The bug
   ....
   FuncTwo(foo);
   ....
}

// Does not expect null
void FuncTwo(Foo foo)
{
   foo.bar();   //null pointer exception
}

Trivial example but consider there are chunks of code you can't see that may 
also use foo that you would need to investigate to see if they set it to 
null, so plenty of code paths to search. Now consider with non-nullables.

void FuncOne(Foo? foo)
{
   ....
   foo = null;   // The bug
   ....
   FuncTwo(enforce(foo));   // [2]
   ....
}

// Does not expect null
void FuncTwo(Foo foo)
{
   foo.bar();
}

[2] Here I am guessing at what people mean by enforce. My assumption is it 
checks to see if foo is null and throws nullpointerexception if so. Else it 
lets to application continue executing and also skips the compiler check 
that we are passing a nullable into a non-nullable.

So first off without enforce [2] would have had a compiler error that would 
have made me investigate this potential bug anyway, whether I should have an 
alternate code path or more in depth look at the design, but lets assume I 
think it should never get to that state because its not valid for it to be 
null at [2] (but it is else where in FuncOne). So on execution we get an 
exception at [2], so we died earlier than we did in the nullable form. So 
not only do we have less to search (a lot less, cos not only does the trace 
give us less but also any other functions that only take non-nullable can 
remove code paths to check making the search area much smaller, sounds 
productive), but also we killed the application earlier before it did even 
more damage (like putting a plane into a dive maybe?). I wanted to point 
that out because I am sure Walter noticed it moved the error from one place 
to another but I don't think anyone has pointed out it is a way to identify 
bad application state earlier (which seems to be the focus for one argument) 
rather than later (seems to be that eventually code paths that allow nulls 
sooner or later turn into ones that don't because otherwise the variables 
are pointless, so by telling the compiler where it turns to a non null path 
you can get it to trigger the exceptions early which I would think would be 
inline with crashing the application when there is bad state).

I also see non-nullables helping track down potential bugs when changing 
variables to non-nullable and removing unnecessary code for vice versa.

Seems to me non-nullable is in the same sort of area as const/immutable. 
Where as immutable or const prevent any data changes from happening to stop 
bad application state, non-nullables prevent null going where its not 
allowed for bad application state with numerous other productivity benefits.

Are these the ramblings of a sleep deprived mad man getting involved with 
things he doesn't understand? you decide :-).



More information about the Digitalmars-d mailing list