May 20, 2013
On 5/19/2013 5:23 PM, John Colvin wrote:
> On Sunday, 19 May 2013 at 20:45:39 UTC, Walter Bright wrote:
>> On 5/19/2013 1:03 PM, Maxim Fomin wrote:
>>> I think there is difference between catching exception and saving
>>> data which you have typed for some period and letting harware
>>> "check" the exception for you meanwile loosing your work.
>>
>> You can catch seg faults. It's easier on Windows, but it's doable on Linux.
>
> What's the rational for not doing this by default in D? Wouldn't a
> MemoryAccessError or similar be better than crashing out with SIGSEGV ?

Writing a seg fault handler under Linux has a large number of weird constraints.

> I have
> no idea about the consequences of this (other than tempting people to catch a
> segfault when they shouldn't, which is pretty much always).

At some point, all the scaffolding and workarounds to try to prevent programmers from having to deal with the underlying reality of how the system works is not appropriate for a systems programming language.
May 20, 2013
On Monday, 20 May 2013 at 01:08:16 UTC, Walter Bright wrote:
> On 5/19/2013 5:28 PM, deadalnix wrote:
>> The error lie in improper
>> initialization of p in the first place, which should never has been null. The
>> example looks dumb as this, you have to imagine the pattern hidden in thousands
>> of LOC.
>
> I would find a design that declared a variable in one place, then initialized it in another, while releasing the lock in between as a bad design pattern to begin with.

I cannot agree more. This is what made tracking the cause of the bug super hard.

> What other default initialized types could be there? What about an int default initialized to 0, yet code in another thread expects it to be some other value? I suspect there'd be a lot more bugs in it than just null pointer initializations.
>
> It might be time to engineer a new pattern so you don't have to inspect thousands of LOC to manually verify correctness.

I didn't programed Apache Cayenne, int he first place. But I had to patch it anyway.
May 20, 2013
On Monday, 20 May 2013 at 00:55:14 UTC, Kenji Hara wrote:
> Unfortunately this is currently not a bug.
> T.init provides "default initialized" object image, and it *does not*
> provide "default constructed" object. The difference is important.
>
> That is already documented in lanugage reference.
> http://dlang.org/property#init
>
>> Note: .init produces a default initialized object, not default
> constructed. That means using .init is sometimes incorrect.
>> 1. If T is a nested struct, the context pointer in T.init is null.
>> 2. If T is a struct which has @disable this();, T.init might return a
> logically incorrect object.
>
> Kenji Hara
>

I think this should be fixed otherwise @disable this() is compromised. What is rationale behind allowing .init?

May 20, 2013
I know at least two cases which T.init is commonly used.

1. Inside predicate template for type T.

template isSomething(T) {
    enum isSomething = is(typeof({
        //T t1;    // not good if T is nested struct, or has @disable this()
        //T t2 = void; auto x = t2;  // not good if T is non-mutable type
        T t = T.init;   // avoid default construct check
        ...use t...
    }));
}

2. Some library utilities that treats object state directly, e.g. std.conv.emplace

Kenji Hara

2013/5/20 Maxim Fomin <maxim@maxim-fomin.ru>

> On Monday, 20 May 2013 at 00:55:14 UTC, Kenji Hara wrote:
>
>> Unfortunately this is currently not a bug.
>> T.init provides "default initialized" object image, and it *does not*
>> provide "default constructed" object. The difference is important.
>>
>> That is already documented in lanugage reference. http://dlang.org/property#init
>>
>>  Note: .init produces a default initialized object, not default
>>>
>> constructed. That means using .init is sometimes incorrect.
>>
>>> 1. If T is a nested struct, the context pointer in T.init is null. 2. If T is a struct which has @disable this();, T.init might return a
>>>
>> logically incorrect object.
>>
>> Kenji Hara
>>
>>
> I think this should be fixed otherwise @disable this() is compromised. What is rationale behind allowing .init?
>
>


May 20, 2013
On Monday, 20 May 2013 at 06:10:22 UTC, Kenji Hara wrote:
> I know at least two cases which T.init is commonly used.
>
> 1. Inside predicate template for type T.
>
> template isSomething(T) {
>     enum isSomething = is(typeof({
>         //T t1;    // not good if T is nested struct, or has @disable this()
>         //T t2 = void; auto x = t2;  // not good if T is non-mutable type
>         T t = T.init;   // avoid default construct check
>         ...use t...
>     }));
> }
>
> 2. Some library utilities that treats object state directly, e.g.
> std.conv.emplace
>
> Kenji Hara

I see. But unfortunately this undermines @disable and defeats arguments for using it. @disable is another feature (like ref and @safe) which cannot be fixed be design.
May 20, 2013
On Sunday, 19 May 2013 at 21:36:17 UTC, Andrei Alexandrescu wrote:
> OK, this is sensible. One question - would you be willing to type symbols as NullType!T instead of T to avoid these issues?

Good question. Probably not. I think it's one of those things were the awkwardness of it not being the default would lead to lack of use (in the same way usage of pure suffers from not being default).
May 20, 2013
On Sun, 19 May 2013 21:02:11 +0200, Idan Arye <GenericNPC@gmail.com> wrote:

> I don't see how Option and Maybe would have helped your bug. The problem was that somewhere in the code the reference was perceived as null while in fact it wasn't

What does that even mean?


> - so now it will be perceived as `None`, and you will have the same problem.

Except that now the code would be forced to handle the None case. In a way,
having nullable by default is like having a stringly typed system:

function foo( s ) {
   return (s + 4) * 2; // Works great when s == "16", falls dead on its
                       // back when s == "goobers".
}

function bar( int i ) {
    return (i + 4) * 2;
}


These functions look very different. That's because they are. One of them
only takes valid parameters, the other takes any old garbage and barfs
when the wrong garbage is given to it.

Of course, if you have a string, and you want to call bar, you need to
convert the string to an int. So you end up with this:

function baz( string s ) {
    return s.parseInt(
        i => bar(i),
        { alert("error"); });
}

Notice how the parseInt function takes two delegates? One of these
(the first) is only called when the string is valid. The other is only
called if the string is invalid. That way, we can be sure that the
failure case is handled.

Exactly the same would be the case for non-nullable pointers - if you
want to convert a nullable pointer to non-nullable, you *have* to
handle the failure case. No two ways about it.

Now, the same example with class references:

int foo(A a) {
   return a.qux(); // Works great when a == new A(), falls dead on its
                   // back when a == null.
}

int bar(NonNull!A a) {
   return a.qux();
}

See how one of these does not blow up in your face (unless you do
something stupid like create a special Nil value that will do exactly
that)? Now, for baz:

int baz(A a) {
    return a.match(
      (NonNull!A a) => bar(a),
      (None) => -1
      );
}


>> And this is the exact problem with nullable by default : plenty of stuff ends up be null is some weird situation that almost never occurs when they are assumed not to be and the program crashes. NullPointerException now return 4 millions result on google, which is probably around once per java developers.
>
> This is not a problem with "nullable by default" - it is a problem with implicit default values. null(or Nil, or None) are the only sane default values for reference types - I think you would agree that having to construct a new blank object as default value for every reference variable would be far worse than null...

If you absolutely cannot initialize the pointer to something sensible,
then use a nullable pointer. But if non-nullable pointers are not
available, or are harder to use than nullable pointer, then people will
use nullable pointers even where non-nullable would have been a much
more fitting choice.



> It does not solve the bug - it is something you HAVE to do given the assumptions. If the reference is not nullable, and you can't set it to it's real value until later in the code, then you have to initialize it to some temporary value.

I don't know enough about the bug to say such things for sure, but I will
say this: If deadalnix solved the bug, he is likely in a much better
position to say anything about what would solve the problem than the rest
of us.

-- 
Simen
May 20, 2013
On Sun, 19 May 2013 20:05:02 +0200, Idan Arye <GenericNPC@gmail.com> wrote:

> These are the assumptions I'm working with:
>   - We can't use a nullable reference
>   - We can't initialize the reference upon declaration to it's real value.

Indeed, if that's the case, then what you're doing is fairly sensible. But
if #2 is true, then #1 should never be true.

-- 
Simen
May 20, 2013
On Sun, 19 May 2013 20:23:21 +0200, Walter Bright <newshound2@digitalmars.com> wrote:

> On 5/19/2013 5:02 AM, Simen Kjaeraas wrote:
>>> By definition? Pointer semantics are what we choose it to mean.
>> Of course. But which definition is saner:
>
> For many types, it is extremely useful to have some sort of "invalid" value for it. null fills that role nicely for pointers, just as nan does for floating point types, and 0xFF does for UTF-8.
>
> There's not anything insane about it. The Nullable type constructor even exists in order to provide such an invalid state for types (like int) which normally do not have one.
>
> Yes, I do understand there's a role for pointers which cannot hold the invalid value.

I contend that not only is there a role for them, but that most pointers
should never be null. Here's two questions that convinced me:

1. How many functions that take a pointer or class reference make sense
   to call with null in that pointer/reference?

2. If pointers were non-nullable by default, how often would you need to
   reach for the nullable one?

I argued in another post that nullable by default is analogous to using a
string instead of an int - any number representable in an int is
representable in a string, *and* the string can represent error states.
But if you only want valid ints, there's no reason to use a string.

-- 
Simen
May 20, 2013
I'm surprised people still have problems with null pointers. I for one am glad D has null by default makes life easy coming from C++ and Java.

I may have missed something but what happens with the following code if I could not have a null pointer?

int*[] pntrs = new int*[10];

Would I need to write something like?

Null!(int*)[] pntrs = new Null!(int*)[10];

Personally, I'd rather have null by default as I find it less noisy and I don't need it spelled out in the code, it is implied.

>> No it sound like initalizing something to null, then initialize it properly, assume all over the place that it is initialized to something else, and in some rare code path it blows up.

OK, so the D gurus kindly introduce for us NotNull!T, Maybe!T, Option!T and SegFault!T (just for me). Now I want to access a pointer, write code using it etc. But I need to manually track at development time whether it is NotNull!T, Null!T, Maybe!T, Option!T or whatever. I cannot just have a pointer anymore, knowing it's initialised to null. Now I realise it needs to change from NotNull!T to Maybe!T...great yet more refactoring. Ok refactoring done (yay sed!) but you know what, now I need to find every access to that pointer and check for null. More error prone than this:

If you are in doubt (i.e. most multi-threaded apps) then check if null, with the added comfort that D has initialised all pointers to NULL for you. If still in doubt, don't use pointers.

If you want non-null pointers (please no) then it is all or nothing. Allowing some pointers null and others not, via Nullable!T or NotNull!T, immediately adds another layer of complexity.

I don't want to hear: D pointers cannot be null...well ok, they can sometimes, it depends, you'll have to read the code. But don't worry, D is very easy to read...

My 1 cent. Disregard if I have totally misunderstood the thread, possible as it is very late! :-)

Cheers,
Stewart