January 23, 2013
On Wednesday, 23 January 2013 at 08:00:05 UTC, Simen Kjaeraas wrote:
> On 2013-45-23 04:01, deadalnix <deadalnix@gmail.com> wrote:
>
>> On Tuesday, 22 January 2013 at 19:07:56 UTC, Simen Kjaeraas wrote:
>
>>> One could argue that the compiler should choose the other constructor, and
>>> even that having default constructors is reasonable. However, D has gone
>>> the route of not having default constructors for structs. Instead,
>>> structs are defined to be trivially constructible from T.init.
>>>
>>
>> Which incompatible with the desire of a NonNull construct.
>
> Really? It's not like you cannot @disable T.init. Please show how this is
> a problem for NonNull!T.
>

Easy :

NonNull!T bar; // bar is null

NonNull!T[] barArray;
barArray.length = 5; // barArray contains null elements.

NonNull!T bar = something;
foo(move(bar));
bar; // bar is null \o/

In fact they is so much way to create null stuff here that it isn't even remotely difficult to create a NonNull that is null;
January 23, 2013
On 2013-43-23 09:01, deadalnix <deadalnix@gmail.com> wrote:

> On Wednesday, 23 January 2013 at 08:00:05 UTC, Simen Kjaeraas wrote:
>> On 2013-45-23 04:01, deadalnix <deadalnix@gmail.com> wrote:
>>
>>> On Tuesday, 22 January 2013 at 19:07:56 UTC, Simen Kjaeraas wrote:
>>
>>>> One could argue that the compiler should choose the other constructor, and
>>>> even that having default constructors is reasonable. However, D has gone
>>>> the route of not having default constructors for structs. Instead,
>>>> structs are defined to be trivially constructible from T.init.
>>>>
>>>
>>> Which incompatible with the desire of a NonNull construct.
>>
>> Really? It's not like you cannot @disable T.init. Please show how this is
>> a problem for NonNull!T.
>>
>
> Easy :
>
> NonNull!T bar; // bar is null

Patently false. @disable this() makes this fail with "initializer required
for type".


> NonNull!T[] barArray;
> barArray.length = 5; // barArray contains null elements.

This is a known bug in the implementation of @disable this().


> NonNull!T bar = something;
> foo(move(bar));
> bar; // bar is null \o/

Except it isn't.


-- 
Simen
January 23, 2013
On Wednesday, 23 January 2013 at 10:28:05 UTC, Simen Kjaeraas wrote:
>> NonNull!T bar = something;
>> foo(move(bar));
>> bar; // bar is null \o/
>
> Except it isn't.

move memcopy T.init . So it will be null.
January 23, 2013
On 2013-48-23 11:01, deadalnix <deadalnix@gmail.com> wrote:

> On Wednesday, 23 January 2013 at 10:28:05 UTC, Simen Kjaeraas wrote:
>>> NonNull!T bar = something;
>>> foo(move(bar));
>>> bar; // bar is null \o/
>>
>> Except it isn't.
>
> move memcopy T.init . So it will be null.

Try it. Without a destructor, move does not revert to T.init. With a destructor you get this:

src\phobos\std\algorithm.d(1564): Error: variable std.algorithm.move!(NotNull!(int*)).move.empty initializer required for type NotNull!(int*)
Error: template instance std.algorithm.move!(NotNull!(int*)) error instantiating

This is what @disable this does, and what it's supposed to do.

-- 
Simen
January 23, 2013
Why is it required to have the .init property? Where is it useful and why was this decision made?
January 23, 2013
On Wednesday, 23 January 2013 at 11:03:45 UTC, Minas Mina wrote:
> Why is it required to have the .init property? Where is it useful and why was this decision made?

.init is very useful in the sense that it represents the raw object, *statically*.

This is useful for declaring statics.

It also makes constructors straightforward, in a simple 2 pass scheme (copy T.init, then run constructor).

More importantly, the T.init state is also the one you are supposed to have post destruction (Or at least, calling a destructor on T.init is supposed to be safe). This allows for some *very* efficient and built-in move semantics:

To move a into b, simply destroy b, memcopy a into b, memcopy T.init over a.

Notice how massively simpler this is compared to C++'s explicit Rvalue references? I *dare* you to try doing that with C++.

"DEFAULT" constructor breaks this entire scheme, as it implies that T.init is not an object's natural state. It jeopardizes a static destructible state.

The problem is that this overlaps with having an "explicit constructor that takes no arguments". This is particularly problematic for objects that have pointers to payloads that represent "shallow" objects. AA's is a prime example of this:

//----
    void insert5(int[int] i)
    {
        i[5] = 5;
    }

    int[int] a;           //Un-initialized T.init state
    int[int] b = [1 : 1]; //initialized
    int[int] c = [1 : 1]; c.remove(1); //Empty but initialized
    insert5(a);
    insert5(b);
    insert5(c);
    assert(a is null);           //Oh no!
    assert(b == [1 : 1, 5 : 5]); //5 correctly appended
    assert(c == [5 : 5]);        //5 correctly copied
//----

In this example, I had to jump through loops to initialize c.
January 23, 2013
On Wednesday, 23 January 2013 at 10:55:43 UTC, Simen Kjaeraas wrote:
> On 2013-48-23 11:01, deadalnix <deadalnix@gmail.com> wrote:
>
>> On Wednesday, 23 January 2013 at 10:28:05 UTC, Simen Kjaeraas wrote:
>>>> NonNull!T bar = something;
>>>> foo(move(bar));
>>>> bar; // bar is null \o/
>>>
>>> Except it isn't.
>>
>> move memcopy T.init . So it will be null.
>
> Try it. Without a destructor, move does not revert to T.init. With a destructor you get this:
>
> src\phobos\std\algorithm.d(1564): Error: variable std.algorithm.move!(NotNull!(int*)).move.empty initializer required for type NotNull!(int*)
> Error: template instance std.algorithm.move!(NotNull!(int*)) error instantiating
>
> This is what @disable this does, and what it's supposed to do.

Ho that is awesome !

Still many way of generating it on the heap, but it seems that the stack is getting better !
January 23, 2013
On 2013-14-23 13:01, deadalnix <deadalnix@gmail.com> wrote:

> On Wednesday, 23 January 2013 at 10:55:43 UTC, Simen Kjaeraas wrote:
>> On 2013-48-23 11:01, deadalnix <deadalnix@gmail.com> wrote:
>>
>>> On Wednesday, 23 January 2013 at 10:28:05 UTC, Simen Kjaeraas wrote:
>>>>> NonNull!T bar = something;
>>>>> foo(move(bar));
>>>>> bar; // bar is null \o/
>>>>
>>>> Except it isn't.
>>>
>>> move memcopy T.init . So it will be null.
>>
>> Try it. Without a destructor, move does not revert to T.init. With a destructor you get this:
>>
>> src\phobos\std\algorithm.d(1564): Error: variable std.algorithm.move!(NotNull!(int*)).move.empty initializer required for type NotNull!(int*)
>> Error: template instance std.algorithm.move!(NotNull!(int*)) error instantiating
>>
>> This is what @disable this does, and what it's supposed to do.
>
> Ho that is awesome !
>
> Still many way of generating it on the heap, but it seems that the stack is getting better !

Hm. The heap. That should be new NotNull!(int*), and
(new NotNull!(int*)[1])[0]. The former is correctly not allowed, and the
latter is a different manifestation of the same bug mentioned previously.

Of course, with casts and pointers, anything is possible.

-- 
Simen
January 23, 2013
On Wednesday, 23 January 2013 at 13:26:47 UTC, Simen Kjaeraas wrote:
> On 2013-14-23 13:01, deadalnix <deadalnix@gmail.com> wrote:
>
>> On Wednesday, 23 January 2013 at 10:55:43 UTC, Simen Kjaeraas wrote:
>>> On 2013-48-23 11:01, deadalnix <deadalnix@gmail.com> wrote:
>>>
>>>> On Wednesday, 23 January 2013 at 10:28:05 UTC, Simen Kjaeraas wrote:
>>>>>> NonNull!T bar = something;
>>>>>> foo(move(bar));
>>>>>> bar; // bar is null \o/
>>>>>
>>>>> Except it isn't.
>>>>
>>>> move memcopy T.init . So it will be null.
>>>
>>> Try it. Without a destructor, move does not revert to T.init. With a destructor you get this:
>>>
>>> src\phobos\std\algorithm.d(1564): Error: variable std.algorithm.move!(NotNull!(int*)).move.empty initializer required for type NotNull!(int*)
>>> Error: template instance std.algorithm.move!(NotNull!(int*)) error instantiating
>>>
>>> This is what @disable this does, and what it's supposed to do.
>>
>> Ho that is awesome !
>>
>> Still many way of generating it on the heap, but it seems that the stack is getting better !
>
> Hm. The heap. That should be new NotNull!(int*), and
> (new NotNull!(int*)[1])[0]. The former is correctly not allowed, and the
> latter is a different manifestation of the same bug mentioned previously.
>
> Of course, with casts and pointers, anything is possible.

I was more thinking about slice and stuff like that. struct on the heap in general are not very handy.
January 23, 2013
On Wednesday, 23 January 2013 at 12:07:48 UTC, monarch_dodra wrote:
> On Wednesday, 23 January 2013 at 11:03:45 UTC, Minas Mina wrote:
>> Why is it required to have the .init property? Where is it useful and why was this decision made?
>
> .init is very useful in the sense that it represents the raw object, *statically*.
>
> This is useful for declaring statics.
>
> It also makes constructors straightforward, in a simple 2 pass scheme (copy T.init, then run constructor).
>
> More importantly, the T.init state is also the one you are supposed to have post destruction (Or at least, calling a destructor on T.init is supposed to be safe). This allows for some *very* efficient and built-in move semantics:
>
> To move a into b, simply destroy b, memcopy a into b, memcopy T.init over a.
>
> Notice how massively simpler this is compared to C++'s explicit Rvalue references? I *dare* you to try doing that with C++.
>
> "DEFAULT" constructor breaks this entire scheme, as it implies that T.init is not an object's natural state. It jeopardizes a static destructible state.
>
> The problem is that this overlaps with having an "explicit constructor that takes no arguments". This is particularly problematic for objects that have pointers to payloads that represent "shallow" objects. AA's is a prime example of this:
>
> //----
>     void insert5(int[int] i)
>     {
>         i[5] = 5;
>     }
>
>     int[int] a;           //Un-initialized T.init state
>     int[int] b = [1 : 1]; //initialized
>     int[int] c = [1 : 1]; c.remove(1); //Empty but initialized
>     insert5(a);
>     insert5(b);
>     insert5(c);
>     assert(a is null);           //Oh no!
>     assert(b == [1 : 1, 5 : 5]); //5 correctly appended
>     assert(c == [5 : 5]);        //5 correctly copied
> //----
>
> In this example, I had to jump through loops to initialize c.

Really good explanation. Thank you. So @disabled default constructor was just a "side effect" of doing those that you described.