View mode: basic / threaded / horizontal-split · Log in · Help
September 26, 2009
Re: Null references redux
Denis Koroskin wrote:
> One more:
> 
> T foo(bool someCondition)
> {
>     T? t;
>     if (someCondition) t = someInitializer();
>     // ...
> 
>     if (t.isNull) { // not initialized yet
>         // ...
>     }
> 
>     return enforce(t); // throws if t is not initialized yet, because 
> foo *must* return a valid value by a contract
> }

It seems to me you've got null references there anyway?

What would you do about:

   T[] a;
   a[i] = foo();

where you want to have unused slots be null (or empty, or nothing)?
September 26, 2009
Re: Null references redux
grauzone wrote:
> Walter Bright wrote:
>> grauzone wrote:
>>> Not so useful.
>>
>> It's *still* far more useful than generating corrupt output and 
>> pretending all is ok.
> 
> But nobody argues in favor of that?

It's implicit in the argument that some default should be used instead. 
That's what I'm trying to point out.

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.

It's a lot like why exception-specifications are a failure. See Bruce 
Eckel's essay on it:
http://www.mindview.net/Etc/Discussions/CheckedExceptions
September 26, 2009
Re: Null references redux
On Sun, 27 Sep 2009 02:49:06 +0400, Walter Bright  
<newshound1@digitalmars.com> wrote:

> Denis Koroskin wrote:
>> On Sun, 27 Sep 2009 01:49:45 +0400, Walter Bright  
>> <newshound1@digitalmars.com> wrote:
>>
>>> The problem with non-nullable references is what do they default to?  
>>> Some "nan" object? When you use a "nan" object, what should it do?  
>>> Throw an exception?
>>>
>>  Oh, my! You don't even know what a non-null default is!
>>  There is a Null Object pattern  
>> (http://en.wikipedia.org/wiki/Null_Object_pattern) - I guess that's  
>> what you are talking about, when you mean "nan object" - but it has  
>> little to do with non-null references.
>
> It's the black hole object. It prevents you from getting a seg fault,  
> but I see no rationale for expecting that an unexpected null object  
> always returning "I succeeded" means your program will operate correctly.
>
> The white hole object, of course, always throws an exception when it is  
> accessed. At least you know something went wrong - but you already have  
> that with null.
>
>
>> With non-null references, you don't have "wrong values", that throw an  
>> exception upon use (although it's clearly possible), you get a correct  
>> value.
>
> You're not getting a correct value, you're getting another default value.


I'm sorry but I can not continue discussion with you like that. You are  
not listening! You are not even trying to understand what I say. We are  
talking *completely* different things here.

Who the hell cares if it's a black or white as long as it is a hole  
object? I tell you that no one is gonna use it, because it's *much* easier  
to do everything right (i.e. initialize a reference to proper value) than  
create Hole classes for each of the class/interface. I can't even imagine  
anyone writing the code like this:

T someFunction(Args someArgs) {
    ISomeInterface someInterface = new  
BlackHoleOfSomeInterfaceThatAlwaysThrows(); // let's initialize that  
variable to some dumb value just to make compiler happy
    // rest of the method body
}

Novices, maybe. But professionals would never do that sin for sure.

*Please* let's go past that pattern, it really has nothing to do with  
proposed non-null by default references.

> If the logic of your program is expecting a prime number > 8, and the  
> null object returns 0, now what?
>

You are talking heresy here. I'm afraid you don't even know what you are  
talking about.

A) There is no such thing as null object. That's bullsh*t! No one ever  
proposed to use those, you did. And now you deny use of them and discuss  
how bad they are.
B) You can't call a function that accepts non-null T if you don't have a  
valid (i.e. non-null) T reference. End of story.

>> If an object may or may not have a valid value, you mark it as  
>> nullable. All the difference is that it's a non-default behavior,  
>> that's it. And a user is now warned, that an object may be not  
>> initialized.
>
> He isn't warned, that's just the problem. The null object happily says  
> "I succeeded" for all input and returns more default values and null  
> objects.
>

Please stop that "null object" pattern propaganda. Did you even read what  
I wrote?

I wrote: if a variable may be not initialized - no problem, make it  
nullable and assign a null! A user is now forced to check that variable  
against a null before dereference and must take appropriate actions if it  
is null.

> What happens if the output of your program then becomes a null object?  
> How are you going to go about tracing that back to its source? That's a  
> lot harder than working backwards from where a null exception originated.
>
> 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. The right thing is to immediately inform the pilot that something  
> went wrong and INSTANTLY SHUT THE BAD SYSTEM DOWN before it does  
> something really, really bad, because now it is in an unknown state. The  
> pilot then follows the procedure he's trained to, such as engage the  
> backup.
>
> A null pointer exception fits right in with that philosophy.
>
> You could think of null exceptions like pain - sure it's unpleasant, but  
> people who feel no pain constantly injure themselves and don't live very  
> long. When I went to the dentist as a kid for the first time, he shot my  
> cheek full of novacaine. After the dental work, I went back to school. I  
> found to my amusement that if I chewed on my cheek, it didn't hurt.
>
> Boy was I sorry about that later <g>.

That's trolling, Walter. I'm sorry, but you are talking non-sense here.
Once again, no one ever proposed use of null object pattern. You imagined  
it and now denying its use.
September 26, 2009
Re: Null references redux
On Sun, 27 Sep 2009 03:01:48 +0400, Walter Bright  
<newshound1@digitalmars.com> wrote:

> Denis Koroskin wrote:
>> One more:
>>  T foo(bool someCondition)
>> {
>>     T? t;
>>     if (someCondition) t = someInitializer();
>>     // ...
>>      if (t.isNull) { // not initialized yet
>>         // ...
>>     }
>>      return enforce(t); // throws if t is not initialized yet, because  
>> foo *must* return a valid value by a contract
>> }
>
> It seems to me you've got null references there anyway?
>
> What would you do about:
>
>     T[] a;
>     a[i] = foo();
>
> where you want to have unused slots be null (or empty, or nothing)?

Easy:

T? foo(); // returns valid object or a null

T?[] a;
a[i] = foo();
September 26, 2009
Re: Null references redux
Walter Bright wrote:
> Denis Koroskin wrote:
>> On Sun, 27 Sep 2009 01:49:45 +0400, Walter Bright 
>> <newshound1@digitalmars.com> wrote:
>>
>>> The problem with non-nullable references is what do they default to? 
>>> Some "nan" object? When you use a "nan" object, what should it do? 
>>> Throw an exception?
>>>
>>
>> Oh, my! You don't even know what a non-null default is!
>>
>> There is a Null Object pattern 
>> (http://en.wikipedia.org/wiki/Null_Object_pattern) - I guess that's 
>> what you are talking about, when you mean "nan object" - but it has 
>> little to do with non-null references.
> 
> It's the black hole object. It prevents you from getting a seg fault, 
> but I see no rationale for expecting that an unexpected null object 
> always returning "I succeeded" means your program will operate correctly.
> 
> The white hole object, of course, always throws an exception when it is 
> accessed. At least you know something went wrong - but you already have 
> that with null.
> 
> 
>> With non-null references, you don't have "wrong values", that throw an 
>> exception upon use (although it's clearly possible), you get a correct 
>> value.
> 
> You're not getting a correct value, you're getting another default 
> value. If the logic of your program is expecting a prime number > 8, and 
> the null object returns 0, now what?
> 
>> If an object may or may not have a valid value, you mark it as 
>> nullable. All the difference is that it's a non-default behavior, 
>> that's it. And a user is now warned, that an object may be not 
>> initialized.
> 
> He isn't warned, that's just the problem. The null object happily says 
> "I succeeded" for all input and returns more default values and null 
> objects.
> 
> What happens if the output of your program then becomes a null object? 
> How are you going to go about tracing that back to its source? That's a 
> lot harder than working backwards from where a null exception originated.
> 
> 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. The right thing is to immediately inform the pilot that something 
> went wrong and INSTANTLY SHUT THE BAD SYSTEM DOWN before it does 
> something really, really bad, because now it is in an unknown state. The 
> pilot then follows the procedure he's trained to, such as engage the 
> backup.
> 
> A null pointer exception fits right in with that philosophy.
> 
> You could think of null exceptions like pain - sure it's unpleasant, but 
> people who feel no pain constantly injure themselves and don't live very 
> long. When I went to the dentist as a kid for the first time, he shot my 
> cheek full of novacaine. After the dental work, I went back to school. I 
> found to my amusement that if I chewed on my cheek, it didn't hurt.
> 
> Boy was I sorry about that later <g>.

Haha that's a nice analogy, I myself was just unable to speak. I guess 
that's what you call undefined behavior!

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.

Jeremie
September 26, 2009
Re: Null references redux
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? Dereferencing a null pointer? Passing a null reference into a function that does not expect it? Storing a null reference in a variable whose later use does not expect one? Unexpectedly getting a null back from a function? ...

You seem to be using the first definition which is meaningless to me. What I need to know is how the null ended up where it was unexpected.

> 
> 1. Immediately stop and produce an indication that the program failed

By most definitions above, D does not do this. I have more to say, but ran out of time to type this. I'll add more later...
September 26, 2009
Re: Null references redux
Jarrett Billingsley:

> Jeremie Pelletier:
> >
> > 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.
> 
> Either use Object? (a nullable reference), or factor out the object
> creation - use a separate method or something.

Using a separate function to initialize an nonnull reference is a possible solution, but we can invent nicer solutions too.

You can have a function where inside an object is nullable but returns a nonnull reference, see the enforce() used by Denis Koroskin. (The compiler also has to recognize as a possible "enforce" an if (foo is null) {...}).

Another possible solution is to use something like a Python "with" block that assures something is done when the block is done:

enforce (Object foo) {
   // foo is nonnull, but inside here it's in a limbo
   if(a == 1)
       foo = new Object1;
   else if(a == 2)
       foo = Object2;
   else
       foo = Object3;
} // when the enforce block ends foo must be initialized
foo.doSomething();

Probably there are other possible solutions.

A better solution is to just allow foo to be undefined until it's written over. To simplify analysis it has to be defined when the scope ends.

Bye,
bearophile
September 26, 2009
Re: Null references redux
Walter Bright wrote:
> The problem with non-nullable references is what do they default to? 
> Some "nan" object? When you use a "nan" object, what should it do? Throw 
> an exception?

This is the mistake. There would no way to default initialize a non-null 
object. I'm surprised you are still saying this, because we discussed 
how NonNull!T could be implemented by disabling its default constructor.

Andrei
September 26, 2009
Re: Null references redux
Walter Bright wrote:
> grauzone wrote:
>> Walter Bright wrote:
>>> grauzone wrote:
>>>> Not so useful.
>>>
>>> It's *still* far more useful than generating corrupt output and 
>>> pretending all is ok.
>>
>> But nobody argues in favor of that?
> 
> It's implicit in the argument that some default should be used instead. 
> That's what I'm trying to point out.
> 
> 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.

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.


Andrei
September 27, 2009
Re: Null references redux
Walter Bright wrote:
> Denis Koroskin wrote:
>> If an object may or may not have a valid value, you mark it as 
>> nullable. All the difference is that it's a non-default behavior, 
>> that's it. And a user is now warned, that an object may be not 
>> initialized.
> 
> He isn't warned, that's just the problem. The null object happily says 
> "I succeeded" for all input and returns more default values and null 
> objects.

This is not the proposal. The proposal was to codify in the type system 
whether a particular object has "null" as a valid value. If a variable 
that cannot be null is not initialized to a non-null value before use, 
that is an error.

It's entirely equivalent to using the current type system with a ton of 
manual contracts requiring that variables not be null. Except the 
contracts are enforced at compile time, not runtime.

A similar concept would be range-bounded integer types, or floating 
point types that cannot be NaN or infinity.
1 2 3 4 5 6 7 8
Top | Discussion index | About this forum | D home