February 09, 2009
Christopher Wright Wrote:

> Michel Fortin wrote:
> > On 2009-02-09 06:41:59 -0500, Ary Borenszweig <ary@esperanto.org.ar> said:
> > 
> >> How would you do this?
> >>
> >> X x;
> >>
> >> if(someCondition) {
> >>    x = new SomeX();
> >> } else {
> >>    x =  new SomeOtherX();
> >> }
> > 
> > Well, the declaration could be transformed to this:
> > 
> >     X x = new X;
> > 
> > But since x is not used before being reassigned (for all code paths), the compiler could just leave it uninitialized until you do the assignement yourself. But then perhaps the "new X" should not be elided unless the constructor and and destructor are pure.
> 
> I don't trust that the compiler would do that.
> 
> > Another question is what happens when X is an abstract class.
> 
> Or when X has no default constructor. Or when X's default constructor allocates non-memory resources. Or when X's default constructor depends on global variables that those conditionals set and segfaults or throws when those globals are not in a consistent state. Or when X is a really huge class or does a lot of computation in its constructor and allocating it takes an unreasonable amount of time.
> 
> Any case in which the default constructor has side effects or preconditions is a problem.

1) It is a valid C++ construct, and I don't remember having any problem with it.

2) It's just a shortcut for Foo foo = new Foo(). If you don't plan to instanciate the instance immediately, i.e. you want to leave it as null, you should mark it as nullable:

Foo? foo; // no ctor call made
February 09, 2009
Denis Koroskin wrote:
> On Mon, 09 Feb 2009 13:48:39 +0300, Alex Burton <alexibu@mac.com> wrote:
> 
>> I think it makes no sense to have nullable pointers in a high level language like D.
>>
>> In D :
>>
>> X x = new X;
>> This is a bit redundant, if we take away the ability to write X x; to mean X x = 0; then we can have X x; mean X x = new X;
>> If the class has a ctor then we can write X x(32); instead of X x = new X(32);
>> Only when the types of the pointer and class are different do we need to write X x = new Y;
>> We can do this syntactically in D because classes cannot be instantiated on the stack (unless scope is used, which I have found a bit pointless, as members are not scope so no deterministic dtor)
>>
>> This makes the code much less verbose and allows code to change from X being a struct to X being a class without having to go around and change all the X x; to X = new X;
>>
>> As I said in the nullable types thread:
>> Passing 0 or 0x012345A or anything else that is not a pointer to an instance of X to a variable declared as X x is the same as mixing in a bicycle when a recipe asks for a cup of olive oil.
>>
>> There are much better, and less error prone ways to write code in a high level language than allowing null pointers.
>>
>> Alex
> 
> I remember Andrei has showed interest in unification of the way value and reference types are instantiated:
> 
> Foo foo(arg1, arg2); // valid instance, be it reference of value type
> Bar bar; // same here (default ctor is called)
> 
> and ditch 'new' keyword altogether.

That would be nice but Walter says he dislikes a dynamic allocation going under the covers.

> Note that you can't delete non-nullable reference so 'delete' keyword is not needed, too (use scope instead). Nullable types, however, may be recycled with e.g. GC.delete(foo);

Delete-ing either non- or yes-nullable references is just as dangerous. IMHO the delete facility of the GC should be eliminated. (Long story.)


Andrei
February 09, 2009

Frits van Bommel wrote:
> Daniel Keep wrote:
>> One option is the "turn everything into an expression" route.  This is what Nemerle (think a functional superset of C#) did, and it's just BEAUTIFULLY expressive.
>>
>>> X x = if( someCondition ) new SomeX(); else new SomeOtherX();
>>
>> Failing that, there's always this (note: didn't do a syntax check on
>> this :P):
>>
>>> X x = ({if(someCondition)
>>>     return new SomeX();
>>>     else return new SomeOtherX();})();
>>
>> Or something to that effect.
> 
> Like this?
>     X x = (someCondition) ? new SomeX() : new SomeOtherX();
> 
> *Much* preferred to delegate trickery, I'd think...

Replace "new SomeX();" with two statements.

  -- Daniel
February 09, 2009
Andrei Alexandrescu Wrote:

> Denis Koroskin wrote:
> > On Mon, 09 Feb 2009 13:48:39 +0300, Alex Burton <alexibu@mac.com> wrote:
> > 
> >> I think it makes no sense to have nullable pointers in a high level language like D.
> >>
> >> In D :
> >>
> >> X x = new X;
> >> This is a bit redundant, if we take away the ability to write X x; to
> >> mean X x = 0; then we can have X x; mean X x = new X;
> >> If the class has a ctor then we can write X x(32); instead of X x =
> >> new X(32);
> >> Only when the types of the pointer and class are different do we need
> >> to write X x = new Y;
> >> We can do this syntactically in D because classes cannot be
> >> instantiated on the stack (unless scope is used, which I have found a
> >> bit pointless, as members are not scope so no deterministic dtor)
> >>
> >> This makes the code much less verbose and allows code to change from X being a struct to X being a class without having to go around and change all the X x; to X = new X;
> >>
> >> As I said in the nullable types thread:
> >> Passing 0 or 0x012345A or anything else that is not a pointer to an
> >> instance of X to a variable declared as X x is the same as mixing in a
> >> bicycle when a recipe asks for a cup of olive oil.
> >>
> >> There are much better, and less error prone ways to write code in a high level language than allowing null pointers.
> >>
> >> Alex
> > 
> > I remember Andrei has showed interest in unification of the way value and reference types are instantiated:
> > 
> > Foo foo(arg1, arg2); // valid instance, be it reference of value type
> > Bar bar; // same here (default ctor is called)
> > 
> > and ditch 'new' keyword altogether.
> 
> That would be nice but Walter says he dislikes a dynamic allocation going under the covers.
> 

How about dynamic closures? It's way much harder to /properly/ determine whether a closure allocates that to determine if Foo foo; allocates But it reduces syntax complexity (by removing one syntax construct) and make structs and classes a little bit more intechangeble, which is a plus, I think.

> > Note that you can't delete non-nullable reference so 'delete' keyword is not needed, too (use scope instead). Nullable types, however, may be recycled with e.g. GC.delete(foo);
> 
> Delete-ing either non- or yes-nullable references is just as dangerous. IMHO the delete facility of the GC should be eliminated. (Long story.)
> 

I competely agree. Don't remember last time I used delete in D.

> 
> Andrei

February 09, 2009
On Mon, 09 Feb 2009 14:41:59 +0300, Ary Borenszweig <ary@esperanto.org.ar> wrote:

[skip]
> How would you do this?
>
> X x;
>
> if(someCondition) {
>    x = new SomeX();
> } else {
>    x =  new SomeOtherX();
> }

Note: I am not talking about an "X x(42);" shortcut now, see the subject.

This is a good question, I think.

One way to do it is as follows:

[code]
T enforce(T? t);

X? tmp; // null

if (someCondition) {
   tmp = new SomeX();
} else {
   tmp = new SomeOtherX();
}

X x = enforce(tmp);
[/code]

But it is too verbose and has one (potentially unsafe) operation (enforce). A check is moved from compile-time to run-time, which is what we wanted to avoid when introduced non-nullable references.

Here is my alternative - just like that, there is no need for extra syntax.

X x;
if(someCondition) {
  x = new SomeX();
} else {
  x =  new SomeOtherX();
}

For example, the following code is *invalid* C#:

[code]
X x;

if (someCondition) {
   X = new SomeX();
}

foo(x); // error:  Use of unassigned local variable 'x'
[/code]

However, if you initialize the variable at all code-paths, then it is ok:

[code]
X x;

if (someCondition) {
   x = new SomeX();
} else {
   x =  new SomeOtherX();
}

foo(x);
[/code]

The only difference is that 'x' is allowed to be null-initialized in C#:

X x = null;
foo(x); // ok, x is /initialized/ (to null)

Making it a compile-time error (x is non-nullable) in D will solve the issue.

Other issue is non-nullable objects as members - how would you make sure that they are properly initialized prior to first access to them?

class Foo {}

class Bar
{
   private Foo x;
   private Foo y;

   this()
   {
       // how would you initialize x and y?
   }
}

A C++ solution would be to initialize them in the initialization list:

Bar::Bar() : x(new Foo()), y(new Foo())
{
}

but it is not flexible enough and we don't have it in D anyway.

As a solution, local variable initialization rules could be extended to member variables - you should have all non-nullable members initialized before you successfully leave the ctor scope and before you pass this to some function or store it in global variable to ensure that 'this' is not visible outside of the ctor scope until all members initialized:

class Bar : Exception
{
   private Foo x;
   private Foo y;
    this()
   {
       //super(""); error, base class ctor may call virtual method (e.g., toString) that potentially may access x or y.

       // gFoo = this; // error: x and y are not initalized, possible use of uninitialized variables detected

       x = new Foo();

       if (someCondition) {
           throw new Exception("Just an example"); // okay, object is not constructed
       }

       // throw this; // error: y is not initialized
       // y = new Foo(this); // error: y is not initialized yet

       y = new Foo(x); // okay, x is fine
             super("");

       someFunc(this); // okay, all members are initialized by now
   }
}

Do you have any other ideas to solve the issue above?

February 09, 2009
Ary Borenszweig Wrote:

> Alex Burton wrote:
> > I think it makes no sense to have nullable pointers in a high level language like D.
> > 
> > In D :
> > 
> > X x = new X;
> > This is a bit redundant, if we take away the ability to write X x; to mean X x = 0; then we can have X x; mean X x = new X;
> > If the class has a ctor then we can write X x(32); instead of X x = new X(32);
> > Only when the types of the pointer and class are different do we need to write X x = new Y;
> > We can do this syntactically in D because classes cannot be instantiated on the stack (unless scope is used, which I have found a bit pointless, as members are not scope so no deterministic dtor)
> > 
> > This makes the code much less verbose and allows code to change from X being a struct to X being a class without having to go around and change all the X x; to X = new X;
> > 
> > As I said in the nullable types thread:
> > Passing 0 or 0x012345A or anything else that is not a pointer to an instance of X to a variable declared as X x is the same as mixing in a bicycle when a recipe asks for a cup of olive oil.
> > 
> > There are much better, and less error prone ways to write code in a high level language than allowing null pointers.
> > 
> > Alex
> 
> How would you do this?
> 
> X x;
> 
> if(someCondition) {
>    x = new SomeX();
> } else {
>    x =  new SomeOtherX();
> }

I would try to never write code like that.
The reason being if the if statement becomes a bit more complicated you risk leaving x = 0;
Better to write a small function:

X getX(bool someCondition)
{
 if(someCondition) {
    return new SomeX();
 } else {
    return new SomeOtherX();
 }
}

That way the compiler will have to tell you if X can be possibly returned null.

Alex
February 10, 2009
Note there exists an *very* similar issue - invariant/immutable variables initialization:

class Bar : Base
{
   invariant this()
   {
       // how would you initialize _x and _y?
       // they should be initialized before 'this' laves the scope
       // and before super() ("Foo x()" might be called by base class)
   }

   invariant Foo x()
   {
       return _x;
   }  

   private invariant Foo _x, _y;
}

My proposed rules apply to this situation quite the same.
February 10, 2009

Denis Koroskin wrote:
> Andrei Alexandrescu Wrote:
> 
>> Denis Koroskin wrote:
>>> On Mon, 09 Feb 2009 13:48:39 +0300, Alex Burton <alexibu@mac.com> wrote:
>>>
>>>> I think it makes no sense to have nullable pointers in a high level language like D.
>>>>
>>>> In D :
>>>>
>>>> X x = new X;
>>>> This is a bit redundant, if we take away the ability to write X x; to
>>>> mean X x = 0; then we can have X x; mean X x = new X;
>>>> If the class has a ctor then we can write X x(32); instead of X x =
>>>> new X(32);
>>>> Only when the types of the pointer and class are different do we need
>>>> to write X x = new Y;
>>>> We can do this syntactically in D because classes cannot be
>>>> instantiated on the stack (unless scope is used, which I have found a
>>>> bit pointless, as members are not scope so no deterministic dtor)
>>>>
>>>> This makes the code much less verbose and allows code to change from X being a struct to X being a class without having to go around and change all the X x; to X = new X;
>>>>
>>>> As I said in the nullable types thread:
>>>> Passing 0 or 0x012345A or anything else that is not a pointer to an
>>>> instance of X to a variable declared as X x is the same as mixing in a
>>>> bicycle when a recipe asks for a cup of olive oil.
>>>>
>>>> There are much better, and less error prone ways to write code in a high level language than allowing null pointers.
>>>>
>>>> Alex
>>> I remember Andrei has showed interest in unification of the way value and reference types are instantiated:
>>>
>>> Foo foo(arg1, arg2); // valid instance, be it reference of value type
>>> Bar bar; // same here (default ctor is called)
>>>
>>> and ditch 'new' keyword altogether.
>> That would be nice but Walter says he dislikes a dynamic allocation going under the covers.
>>
> 
> How about dynamic closures? It's way much harder to /properly/ determine whether a closure allocates that to determine if Foo foo; allocates But it reduces syntax complexity (by removing one syntax construct) and make structs and classes a little bit more intechangeble, which is a plus, I think.
> 
>>> Note that you can't delete non-nullable reference so 'delete' keyword is not needed, too (use scope instead). Nullable types, however, may be recycled with e.g. GC.delete(foo);
>> Delete-ing either non- or yes-nullable references is just as dangerous. IMHO the delete facility of the GC should be eliminated. (Long story.)
>>
> 
> I competely agree. Don't remember last time I used delete in D.
> 
>> Andrei

I've used it for managing very large chunks of memory that I don't want hanging around.  Access to this memory is generally mediated by small proxy object using reference counting so I know when it's OK to nuke that big chunk.

GC is wonderful, but there are times where you just can't trust it.

  -- Daniel
February 10, 2009
Daniel Keep wrote:
> 
> Denis Koroskin wrote:
>> Andrei Alexandrescu Wrote:
>>
>>> Denis Koroskin wrote:
>>>> On Mon, 09 Feb 2009 13:48:39 +0300, Alex Burton <alexibu@mac.com> wrote:
>>>>
>>>>> I think it makes no sense to have nullable pointers in a high level language like D.
>>>>>
>>>>> In D :
>>>>>
>>>>> X x = new X;
>>>>> This is a bit redundant, if we take away the ability to write X x; to mean X x = 0; then we can have X x; mean X x = new X;
>>>>> If the class has a ctor then we can write X x(32); instead of X x = new X(32);
>>>>> Only when the types of the pointer and class are different do we need to write X x = new Y;
>>>>> We can do this syntactically in D because classes cannot be instantiated on the stack (unless scope is used, which I have found a bit pointless, as members are not scope so no deterministic dtor)
>>>>>
>>>>> This makes the code much less verbose and allows code to change from X being a struct to X being a class without having to go around and change all the X x; to X = new X;
>>>>>
>>>>> As I said in the nullable types thread:
>>>>> Passing 0 or 0x012345A or anything else that is not a pointer to an instance of X to a variable declared as X x is the same as mixing in a bicycle when a recipe asks for a cup of olive oil.
>>>>>
>>>>> There are much better, and less error prone ways to write code in a high level language than allowing null pointers.
>>>>>
>>>>> Alex
>>>> I remember Andrei has showed interest in unification of the way value and reference types are instantiated:
>>>>
>>>> Foo foo(arg1, arg2); // valid instance, be it reference of value type
>>>> Bar bar; // same here (default ctor is called)
>>>>
>>>> and ditch 'new' keyword altogether.
>>> That would be nice but Walter says he dislikes a dynamic allocation going under the covers.
>>>
>> How about dynamic closures? It's way much harder to /properly/ determine whether a closure allocates that to determine if Foo foo; allocates But it reduces syntax complexity (by removing one syntax construct) and make structs and classes a little bit more intechangeble, which is a plus, I think.
>>
>>>> Note that you can't delete non-nullable reference so 'delete' keyword is not needed, too (use scope instead). Nullable types, however, may be recycled with e.g. GC.delete(foo);
>>> Delete-ing either non- or yes-nullable references is just as dangerous. IMHO the delete facility of the GC should be eliminated. (Long story.)
>>>
>> I competely agree. Don't remember last time I used delete in D.
>>
>>> Andrei
> 
> I've used it for managing very large chunks of memory that I don't want
> hanging around.  Access to this memory is generally mediated by small
> proxy object using reference counting so I know when it's OK to nuke
> that big chunk.
> 
> GC is wonderful, but there are times where you just can't trust it.
> 
>   -- Daniel

Sure. My suggested framework is one in which you'd use malloc for those allocations. Then you can free. But plopping delete in the midst of a GC system... that's just uncalled for.

Andrei
February 10, 2009
== Quote from Andrei Alexandrescu (SeeWebsiteForEmail@erdani.org)'s article
> Daniel Keep wrote:
> >
> > Denis Koroskin wrote:
> >> Andrei Alexandrescu Wrote:
> >>
> >>> Denis Koroskin wrote:
> >>>> On Mon, 09 Feb 2009 13:48:39 +0300, Alex Burton <alexibu@mac.com> wrote:
> >>>>
> >>>>> I think it makes no sense to have nullable pointers in a high level language like D.
> >>>>>
> >>>>> In D :
> >>>>>
> >>>>> X x = new X;
> >>>>> This is a bit redundant, if we take away the ability to write X x; to
> >>>>> mean X x = 0; then we can have X x; mean X x = new X;
> >>>>> If the class has a ctor then we can write X x(32); instead of X x =
> >>>>> new X(32);
> >>>>> Only when the types of the pointer and class are different do we need
> >>>>> to write X x = new Y;
> >>>>> We can do this syntactically in D because classes cannot be
> >>>>> instantiated on the stack (unless scope is used, which I have found a
> >>>>> bit pointless, as members are not scope so no deterministic dtor)
> >>>>>
> >>>>> This makes the code much less verbose and allows code to change from X being a struct to X being a class without having to go around and change all the X x; to X = new X;
> >>>>>
> >>>>> As I said in the nullable types thread:
> >>>>> Passing 0 or 0x012345A or anything else that is not a pointer to an
> >>>>> instance of X to a variable declared as X x is the same as mixing in a
> >>>>> bicycle when a recipe asks for a cup of olive oil.
> >>>>>
> >>>>> There are much better, and less error prone ways to write code in a high level language than allowing null pointers.
> >>>>>
> >>>>> Alex
> >>>> I remember Andrei has showed interest in unification of the way value and reference types are instantiated:
> >>>>
> >>>> Foo foo(arg1, arg2); // valid instance, be it reference of value type
> >>>> Bar bar; // same here (default ctor is called)
> >>>>
> >>>> and ditch 'new' keyword altogether.
> >>> That would be nice but Walter says he dislikes a dynamic allocation going under the covers.
> >>>
> >> How about dynamic closures? It's way much harder to /properly/ determine
whether a closure allocates that to determine if Foo foo; allocates But it reduces syntax complexity (by removing one syntax construct) and make structs and classes a little bit more intechangeble, which is a plus, I think.
> >>
> >>>> Note that you can't delete
> >>>> non-nullable reference so 'delete' keyword is not needed, too (use scope
> >>>> instead). Nullable types, however, may be recycled with e.g.
> >>>> GC.delete(foo);
> >>> Delete-ing either non- or yes-nullable references is just as dangerous. IMHO the delete facility of the GC should be eliminated. (Long story.)
> >>>
> >> I competely agree. Don't remember last time I used delete in D.
> >>
> >>> Andrei
> >
> > I've used it for managing very large chunks of memory that I don't want hanging around.  Access to this memory is generally mediated by small proxy object using reference counting so I know when it's OK to nuke that big chunk.
> >
> > GC is wonderful, but there are times where you just can't trust it.
> >
> >   -- Daniel
> Sure. My suggested framework is one in which you'd use malloc for those
> allocations. Then you can free. But plopping delete in the midst of a GC
> system... that's just uncalled for.
> Andrei

But then, you can't delete, for example, builtin arrays.  Furthermore, if you use C's malloc/free, you have to be really anal about making sure all code paths free the memory.  With the ability to manually free GC managed memory, it becomes ok to miss an infrequently taken code path, like an exception, because it's just a performance optimization.  Lastly, using multiple heaps like this seems kind of inefficient.  There could be tons of free space on the GC heap, and you'd still be allocating on the C heap.

I think it's fine to remove the delete keyword, but at least keep the GC.free library function.