May 19, 2013
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.
May 19, 2013
On 5/19/2013 10:41 AM, deadalnix wrote:
> I have bunch of code that goes like :
>
> auto oldVar = var;
> scope(exit) var = oldVar;
>
> This is begging for a RAII solution where I pass var as template parameter but
> would require default constructor. This is an actual problem I have right now as
> all save/restore are harder and harder to keep in sync for no reason and
> generate a lot of boilerplate.
>
> This is a problem I have right now that default constructor would solve, and
> this isn't the first time I hit that need.

oldVar isn't being default constructed in your example, nor can I see why you'd need a default constructor in order to use RAII for save/restore.

May 19, 2013
On Sunday, 19 May 2013 at 18:23:22 UTC, Walter Bright 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.
>

I don't wanted to bring that up because I thought it would confuse people, but yes, 0xFF for char is the exact same problem and I argue in the same direction : require explicit initialization.

> 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.
>

If something can be null, you MUST do something to handle specifically the null case. D completely fail to ensure that.

void buzz(Foo f) {
    f.foo(); // Rely in faith. It is invalid and way easier to write than the valid code, which is THE recipe for it to spread.
}
May 19, 2013
On Sunday, 19 May 2013 at 18:27:08 UTC, Walter Bright wrote:
> oldVar isn't being default constructed in your example, nor can I see why you'd need a default constructor in order to use RAII for save/restore.

I need to save the value at construction and restore at destruction. I don't need any runtime parameter at construction.
May 19, 2013
On 5/19/2013 10:35 AM, deadalnix wrote:
> On Sunday, 19 May 2013 at 13:13:07 UTC, Andrei Alexandrescu wrote:
>> No, the race condition would have stayed.
>
> That is ridiculous. non nullable would have made the bug non existent, and even
> without race condition the problem would exists. a reference is null, it
> container shared, then set to something else. You can put barriers all over the
> place to make that sequentially consistent that it wouldn't change anything and
> the bug would still arise.

I agree with Andrei that eliminating null is not going to make a race condition into a thread safe one. You've got a serious bug that you may succeed in hiding by eliminating the null, but it's still there. From your description, I suspect the code is suffering from the double-checked locking bug (which can appear in very subtle forms).


> You also never provided any convincing solution to the safety hole. We can't
> even add check only on some edges cases as D also have values types. The only
> solution we are left with that is really safe is to null check every dereference
> or give up on @safe.

Please don't make us guess what exactly you mean by this.

May 19, 2013
On 2013-05-19 19:41, deadalnix wrote:

> I have bunch of code that goes like :
>
> auto oldVar = var;
> scope(exit) var = oldVar;
>
> This is begging for a RAII solution where I pass var as template
> parameter but would require default constructor. This is an actual
> problem I have right now as all save/restore are harder and harder to
> keep in sync for no reason and generate a lot of boilerplate.
>
> This is a problem I have right now that default constructor would solve,
> and this isn't the first time I hit that need.

You can do something like this:

void restore (alias value, alias dg) ()
{
    auto tmp = value;

    scope (exit)
        value = tmp;

    dg();
}

int a;

void foo () { a = 4 };

void main ()
{
    a = 3;
    restore!(a, {
        foo();
    });
}

The syntax isn't that pretty but it should work. I wish D had better syntax for this, something like:

restore(a) {
    foo();
}

-- 
/Jacob Carlborg
May 19, 2013
On Sunday, 19 May 2013 at 18:22:16 UTC, deadalnix wrote:
> On Sunday, 19 May 2013 at 18:05:03 UTC, Idan Arye 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.
>>
>
> If you can't initialize the value, you got to assume when you use it that it may not have been initialized and handle that case. You need either an Option (where you have to be explicit about what you do when the thing is null) or a Maybe (where null is ignored and Maybe "contaminate" every result depending on a maybe value).

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 - so now it will be perceived as `None`, and you will have the same problem.

>> The first assumption is required because we want to describe how the bug scenario deadalnix brought up would look like if references were non-nullable. The second assumption is required because if we could initialize the reference upon declaration to it's real value, we should have just done it in the first place and avoid the whole race hazard.
>>
>
> But that is the whole point ! The damn thing should have been initialized in the first place to avoid the bug. And this should have been caught at compile time with any sane type system.
>
> 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...

>> Now, I'm not saying the solution I presented is good - I'm trying to show that given those two assumptions, we are forced to use this bad solution.
>
> This solution is complex, do not make any sense in a strongly typed language and don't even solve the presented case.

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.
May 19, 2013
On 5/19/13 1:41 PM, deadalnix wrote:
> On Sunday, 19 May 2013 at 04:57:15 UTC, Walter Bright wrote:
>> On 5/18/2013 8:54 PM, deadalnix wrote:
>>> On Sunday, 19 May 2013 at 01:20:31 UTC, Walter Bright wrote:
>>>> I understand that. But the rationale you gave for having a default
>>>> constructor
>>>> was to be able to disable default construction.
>>>
>>> RAII or construction based on template parameters.
>>
>> I know what default constructors are used for in C++. That wasn't what
>> I asked, though. I asked for compelling rationale.
>>
>
> I have bunch of code that goes like :
>
> auto oldVar = var;
> scope(exit) var = oldVar;
>
> This is begging for a RAII solution where I pass var as template
> parameter but would require default constructor.

No need for a default constructor. You pass the current value as a constructor parameter.

Andrei


May 19, 2013
On Sunday, 19 May 2013 at 18:46:31 UTC, Walter Bright wrote:
>> You also never provided any convincing solution to the safety hole. We can't
>> even add check only on some edges cases as D also have values types. The only
>> solution we are left with that is really safe is to null check every dereference
>> or give up on @safe.
>
> Please don't make us guess what exactly you mean by this.

This isn't new and I discussed that again and again.

When you dereference null, you hit the first plage, which is protected on most systems. But if you access an element with sufficient offset you bypass all protections provided by the type system and you are back in unsafe world.

And no, putting nullcheck on access of field of sufficient offset (as propose dby Andrei) isn't enough because we have value types. Consider :

S[BIG_NUMBER]* a;
auto s = &(*a[SLIGHTLY_BELLOW_CHECK_OFFSET]);
s.fieldAccess; // May not have enough offset to trigget null check, but still can be usnafe

See bug reports :
http://d.puremagic.com/issues/show_bug.cgi?id=3677
http://d.puremagic.com/issues/show_bug.cgi?id=5176
May 19, 2013
On 5/19/13 1:35 PM, deadalnix wrote:
> On Sunday, 19 May 2013 at 13:13:07 UTC, Andrei Alexandrescu wrote:
>> On 5/19/13 9:11 AM, deadalnix wrote:
>>> It is both a race condition and a null problem.
>>
>> No, it's just a race condition.
>>
>>> And having non nullable
>>> type would have been a compile time error instead of days of debugging.
>>
>> No, the race condition would have stayed.
>>
>
> That is ridiculous.  non nullable would have made the bug non existent,
> and even without race condition the problem would exists. a reference is
> null, it container shared, then set to something else. You can put
> barriers all over the place to make that sequentially consistent that it
> wouldn't change anything and the bug would still arise.

No, your argument is ridiculous. You make a yarn with precious little detail that describes for everything everyone knows a textbook race condition, essentially ask that you are taking by your word that non-null would miraculously solve it, and, to add insult to injury, and when we don't buy it, you put the burden of proof on us. This is quite a trick, my hat is off to you.

> You also never provided any convincing solution to the safety hole.

What's the safety hole? Objects of large static size?

> We
> can't even add check only on some edges cases as D also have values
> types. The only solution we are left with that is really safe is to null
> check every dereference or give up on @safe.

How about using NonNull. We won't change the language at this point to make non-nullable references by default. Even you acknowledged that that's not practical. So now you contradict your own affirmation. What exactly do you sustain, and what are you asking for?

> I encourage you to look at this :
> http://www.infoq.com/presentations/Null-References-The-Billion-Dollar-Mistake-Tony-Hoare

I read it. I don't buy it. Yeah, it's a point, but it's largely exaggerated for dramatic purposes.

> Most new languages removed nullable by default, or limited its uses
> (scala for instance, allow for null for limited scope).

So what do you realistically think we should do, seeing that we're aiming at stability?

> I once again want to get attention on the fact that GC change everything
> in regard to reference, and that the C++ situation is a bad example.

I don't understand this.


Andrei