September 26, 2009
On Sat, Sep 26, 2009 at 6:10 PM, Walter Bright <newshound1@digitalmars.com> wrote:
> Jarrett Billingsley wrote:
>>
>> It wouldn't. The compiler wouldn't allow it. It would force you to initialize it. That is the entire point of nonnull references.
>
> Initialize it to what?
>
> A user-defined default object? What should happen if that default object is accessed? Throw an exception? <g>

The point of using a nonnull type is that you *never expect it to be null ever*. So you would be initializing it to some useful object. If you *want* null, you'd use a nullable reference.

> How would you define an "empty" slot in a data structure?

A nullable reference.
September 26, 2009
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.

Indeed. I was just commenting in how badly the current D implementation handles it, and how useless the result is.

>> 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?
September 26, 2009
grauzone wrote:
> You just have to make sure the non-nullable reference is definitely assigned.

See my reply to Denis Koroskin on that.
September 26, 2009
Sat, 26 Sep 2009 17:59:45 -0400, Jeremie Pelletier thusly wrote:

> 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();
> }

I just LOVE to see questions like these ;) You still have SO much to learn. Go grab the 'purely functional data structures' by chris okasaki from the nearest library and try how many pages you can read before your head explodes. No, it is a purely enlightening process actually :)
September 26, 2009
Sun, 27 Sep 2009 02:15:33 +0400, Denis Koroskin thusly wrote:

> Until the, non-nullable references are too hard to use to become useful, because you'll end up with a lot of initializer functions:
> 
> void foo(int a) {
> 	Object initializeFoo() {
> 		if (a == 1) return new Object1();
> 		if (a == 2) return new Object2();
> 		return new Object3();
>          }
> 
> 	Object foo = initializeFoo();
> 	foo.doSomething();
> }
> 
> I actually believe the code is more clear that way, but there are cases when you can't do it (initialize a few variables, for example)

Having a functional switch() helps a lot. I write code like this every day:

  val foo = predicate.match {
    case 1 => new Object1
    case 2 => new Object2("foo", "bar")
    case _ => new DefaultObject
  }

  foo.doSomething

I also rarely have runtime bugs these days.
September 26, 2009
language_fan wrote:
> Sat, 26 Sep 2009 14:49:45 -0700, Walter Bright thusly 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?
> 
> Well typically if your type system supports algebraic types, you can define a higher order Optional type as follows:
> 
>   type Optional T = Some T | Nothing
> 
> Now a safe nullable reference type would look like
> 
>   Optional (T*)
> 
> The whole point is to make null pointer tests explicit.

But people are objecting to having to test for null pointers.

> You can pass around the optional type freely, and only on the actual use site you need to pattern match it to see if it's a null pointer:
> 
>   void foo(SafeRef[int] a) {
>     match(a) {
>       case Nothing => // handle null pointer
>       case Some(b) => return b + 2;
>     }
>   }
> 
> The default initialization of this type is Nothing.

I don't see the improvement.

> Some data structures can be initialized in a way that null pointers don't exist. In these cases you can use a type that does not have the 'Nothing' form. This can lead to nice optimizations. There is no default value, cause default initialization can never occur.

Seems like a large restriction on data structures to build that requirement into the language. It would also make it difficult to transfer code from Java or C++ to D.
September 26, 2009
On Sun, 27 Sep 2009 02:03:40 +0400, Walter Bright
<newshound1@digitalmars.com> wrote:

> Denis Koroskin wrote:
>> I don't understand you. You say you prefer 1, but describe the path D currently takes, which is 2!
>>  dchar d; // not initialized
>> writeln(d); // Soldier on and silently produce garbage output
>
> d is initialized to the "invalid" unicode bit pattern of 0xFFFF. You'll see this if you put a printf in. The bug here is in writeln failing to recognize the invalid value.
>
> http://d.puremagic.com/issues/show_bug.cgi?id=3347
>

Change dchar to float or an int. It's still not initialized (well,
default-initialized to some garbage, which may or may not be okay for a
programmer).

>> I don't see at all how is it related to a non-null default.
>
> Both are attempts to use invalid values.
>

No.

>> Non-null default is all about avoiding erroneous situations, enforcing program correctness and stability. You solve an entire class of problem: NullPointerException.
>
> No, it just papers over the problem. The actual problem is the user failed to initialize it to a value that makes sense for his program. Setting it to a default value does not solve the problem.
>
> Let's say the language is changed so that:
>
>     int i;
>
> is now illegal, and generates a compile time error message. What do you suggest the user do?
>
>     int i = 0;
>

1) We are talking about non-null *references* here.
2) I'd suggest user to initialize it to a proper value.

"int i;" is not the whole function, is it? All I say is "i" should be
initialized before accessed, and that fact should be statically enforced
by a compiler.

> The compiler now accepts the code. But is 0 the correct value for the program? I guarantee you that programmers will simply insert "= 0" to get it to pass compilation, even if 0 is an invalid value for i for the logic of the program. (I guarantee it because I've seen it over and over, and the bugs that result.)
>

This is absolutely irrelevant to non-null reference types. Programmer can't write
"Object o = null;" to cheat on the type system.
September 26, 2009
On Sun, 27 Sep 2009 02:18:15 +0400, Walter Bright <newshound1@digitalmars.com> wrote:

> Denis Koroskin wrote:
>>> If you disallow null references what would "Object foo;" initialize to then?
>> Nothing. It's a compile-time error.
>
> Should:
>
>     int a;
>
> be disallowed, too? If not (and explain why it should behave differently), what about:
>
>     T a;
>
> in generic code?

Functional languages don't distinguish between the two (reference or not). We were discussing "non-null by default"-references because it's far less radical change to a language that "non-null by default" for all types.

Once again, you are taking code out of the context. It is worthless to discuss "int a;" on its own.
I'll try to but the context back and show a few concrete examples (where T is a generic type):

void foo()
{
    T t;
}

Results in: error (Unused variable 't').

T foo(bool someCondition)
{
    T t;
    if (someCondition) t = someInitializer();

    return t;
}

Results in: error (Use of potentially unassigned variable 't')

T foo(bool someCondition)
{
    T t;
    if (someCondition) t = someInitializer();
    else t = someOtherInitializer();

    return t;
}

Results in: successful compilation
September 26, 2009
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>.
September 26, 2009
On Sun, 27 Sep 2009 02:43:05 +0400, Denis Koroskin <2korden@gmail.com> wrote:

> On Sun, 27 Sep 2009 02:18:15 +0400, Walter Bright <newshound1@digitalmars.com> wrote:
>
>> Denis Koroskin wrote:
>>>> If you disallow null references what would "Object foo;" initialize to then?
>>> Nothing. It's a compile-time error.
>>
>> Should:
>>
>>     int a;
>>
>> be disallowed, too? If not (and explain why it should behave differently), what about:
>>
>>     T a;
>>
>> in generic code?
>
> Functional languages don't distinguish between the two (reference or not). We were discussing "non-null by default"-references because it's far less radical change to a language that "non-null by default" for all types.
>
> Once again, you are taking code out of the context. It is worthless to discuss "int a;" on its own.
> I'll try to but the context back and show a few concrete examples (where T is a generic type):
>
> void foo()
> {
>      T t;
> }
>
> Results in: error (Unused variable 't').
>
> T foo(bool someCondition)
> {
>      T t;
>      if (someCondition) t = someInitializer();
>
>      return t;
> }
>
> Results in: error (Use of potentially unassigned variable 't')
>
> T foo(bool someCondition)
> {
>      T t;
>      if (someCondition) t = someInitializer();
>      else t = someOtherInitializer();
>
>      return t;
> }
>
> Results in: successful compilation

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
}