www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Proposal : allocations made easier with non nullable types.

reply Alex Burton <alexibu mac.com> writes:
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
Feb 09 2009
next sibling parent reply "Denis Koroskin" <2korden gmail.com> writes:
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. 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); That said, I'd like to see an experimental version of DMD with these featured enabled. It looks nice at first glance, but time and experience is needed to get better understanding of this.
Feb 09 2009
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
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
Feb 09 2009
parent reply Denis Koroskin <2korden gmail.com> writes:
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
Feb 09 2009
parent reply Daniel Keep <daniel.keep.lists gmail.com> writes:
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
Feb 09 2009
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
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
Feb 09 2009
next sibling parent reply dsimcha <dsimcha yahoo.com> writes:
== 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.
Feb 09 2009
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
dsimcha wrote:
 == Quote from Andrei Alexandrescu (SeeWebsiteForEmail erdani.org)'s article
 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.
You oughtn't.
 Furthermore, if you use
 C's malloc/free, you have to be really anal about making sure all code paths
free
 the memory.
I agree. But the underlying idea is that those places are few and far apart.
 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.
The problem is, many (most?) GCs don't support manual deletion. That's a quirk rather endemic to D's implementation. Relying on it is not wise because other allocators will implement it as a do-nothing. But the real problem is that delete creates a safety hole. I'd rather keep at least the GC heap as free from corruption as possible.
 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.
IMHO that is too unsafe for any of these arguments to convince me. Andrei
Feb 09 2009
parent reply Chad J <gamerchad __spam.is.bad__gmail.com> writes:
Besides safety concerns, what does forbidding manual deletion enable GC
implementations to do?
Feb 09 2009
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Chad J wrote:
 Besides safety concerns, what does forbidding manual deletion enable GC
 implementations to do?
Foregoing delete affords the GC more implementation options. Andrei
Feb 09 2009
next sibling parent reply Chad J <gamerchad __spam.is.bad__gmail.com> writes:
Andrei Alexandrescu wrote:
 Chad J wrote:
 Besides safety concerns, what does forbidding manual deletion enable GC
 implementations to do?
Foregoing delete affords the GC more implementation options. Andrei
Such as? I'm not trying to be antagonistic--manual deletion is a nifty feature to me, so if it gets designed away I'd appreciate knowing what great advantages justified such a sacrifice.
Feb 09 2009
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Chad J wrote:
 Andrei Alexandrescu wrote:
 Chad J wrote:
 Besides safety concerns, what does forbidding manual deletion enable GC
 implementations to do?
Foregoing delete affords the GC more implementation options. Andrei
Such as? I'm not trying to be antagonistic--manual deletion is a nifty feature to me, so if it gets designed away I'd appreciate knowing what great advantages justified such a sacrifice.
You need to simply go through a good tutorial on GC implementations, and consider the added costs of offering a manual delete primitive. Andrei
Feb 09 2009
parent reply Leandro Lucarella <llucax gmail.com> writes:
Andrei Alexandrescu, el  9 de febrero a las 22:20 me escribiste:
 Chad J wrote:
Andrei Alexandrescu wrote:
Chad J wrote:
Besides safety concerns, what does forbidding manual deletion enable GC
implementations to do?
Foregoing delete affords the GC more implementation options. Andrei
Such as? I'm not trying to be antagonistic--manual deletion is a nifty feature to me, so if it gets designed away I'd appreciate knowing what great advantages justified such a sacrifice.
You need to simply go through a good tutorial on GC implementations, and consider the added costs of offering a manual delete primitive.
I did read "Garbage Collection: Algorithms for Automatic Dynamic Memory Management"[1] (several times =P) and some papers on the subject and I honestly don't recall any big issues about it. The only case I can think of if copy/compacting collectors that use the heap as a stack when allocating, where freeing a cell in the middle clearly breaks the stack allocation. But in such cases I think delete could be a NOP, just like for now genCollect() really do a fullCollect() because no generational collection is present at the moment, the interface is there. [1] http://www.cs.kent.ac.uk/people/staff/rej/gcbook/gcbook.html -- Leandro Lucarella (luca) | Blog colectivo: http://www.mazziblog.com.ar/blog/ ---------------------------------------------------------------------------- GPG Key: 5F5A8D05 (F8CD F9A7 BF00 5431 4145 104C 949E BFB6 5F5A 8D05) ---------------------------------------------------------------------------- More people die from a champagne-cork popping, than from poison spiders
Feb 11 2009
parent reply grauzone <none example.net> writes:
Leandro Lucarella wrote:
 Andrei Alexandrescu, el  9 de febrero a las 22:20 me escribiste:
 Chad J wrote:
 Andrei Alexandrescu wrote:
 Chad J wrote:
 Besides safety concerns, what does forbidding manual deletion enable GC
 implementations to do?
Foregoing delete affords the GC more implementation options. Andrei
Such as? I'm not trying to be antagonistic--manual deletion is a nifty feature to me, so if it gets designed away I'd appreciate knowing what great advantages justified such a sacrifice.
You need to simply go through a good tutorial on GC implementations, and consider the added costs of offering a manual delete primitive.
I did read "Garbage Collection: Algorithms for Automatic Dynamic Memory Management"[1] (several times =P) and some papers on the subject and I honestly don't recall any big issues about it. The only case I can think of if copy/compacting collectors that use the heap as a stack when allocating, where freeing a cell in the middle clearly breaks the stack allocation.
The only issue I see is with copying garbage collectors. But even then, it would be possible to introduce a "force garbage" flag, which causes the collector to ignore a mark bit. Also, D will never ever get a copying garbage collector (except if it's ported to .net, which would break everything else too), so why care about it?
 But in such cases I think delete could be a NOP, just like for now
 genCollect() really do a fullCollect() because no generational collection
 is present at the moment, the interface is there.
 
 [1] http://www.cs.kent.ac.uk/people/staff/rej/gcbook/gcbook.html
 
Feb 11 2009
parent Chad J <gamerchad __spam.is.bad__gmail.com> writes:
grauzone wrote:
 
 Also, D will never ever get a copying garbage collector (except if it's
 ported to .net, which would break everything else too), so why care
 about it?
 
Never is a strong word! Though even if D did get a copying collector it would be come kind of hybrid that also has a conservative collector for backup. Of course we'll probably be on D 4.0 (or later) by the time that happens. Whatever.
Feb 11 2009
prev sibling parent reply dsimcha <dsimcha yahoo.com> writes:
== Quote from Andrei Alexandrescu (SeeWebsiteForEmail erdani.org)'s article
 Chad J wrote:
 Besides safety concerns, what does forbidding manual deletion enable GC
 implementations to do?
Foregoing delete affords the GC more implementation options. Andrei
Let's assume this is true and that these options are things that are useful to a language like D. Worst case scenario, a GC implementation could just ignore calls to GC.free(). GC.free() could just be implementation defined as either frees memory or does nothing. As far as safety, D is a systems programming language. If you really want to manually delete a piece of GC-allocated memory, you should be able to. It's good for a systems language to make it harder to screw up _by accident_ (I'm all for getting rid of the delete keyword to make deleting GC-allocated memory less obvious), but if you insist on doing something, the language should assume you have a good reason. Deleting GC allocated memory is an advanced technique that you do at your own risk, just like using C's malloc/free, using pointers, turning off bounds checking, casting, using inline assembly, not initializing variables at declaration, and all the other dangerous but sometimes useful stuff that "safe" languages won't let you do. Disabling this wouldn't absolutely prevent corruption of the GC heap since D still has pointers, which in principle could be made to point anywhere, and since one could still do something stupid like store the only pointer to an object in a region not scanned by the GC via casting or something. The bottom line is that I fail to see how deleting GC-allocated memory is any different than other dangerous but useful things that can be done in D, and unless we want to make D a completely safe language at the expense of the flexibility that its unsafeness offers, I don't understand why you're so opposed to it.
Feb 09 2009
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
dsimcha wrote:
 == Quote from Andrei Alexandrescu (SeeWebsiteForEmail erdani.org)'s article
 Chad J wrote:
 Besides safety concerns, what does forbidding manual deletion enable GC
 implementations to do?
Foregoing delete affords the GC more implementation options. Andrei
Let's assume this is true and that these options are things that are useful to a language like D. Worst case scenario, a GC implementation could just ignore calls to GC.free(). GC.free() could just be implementation defined as either frees memory or does nothing.
Sure, but making GC.free() offer no guarantee makes it rather useless. It's actually worse than useless because you see a call to GC and you don't know whether or not that could mess up your heap, and you also don't know whether or not that can get you any memory.
 As far as safety, D is a systems programming language.  If you really want to
 manually delete a piece of GC-allocated memory, you should be able to.  It's
good
 for a systems language to make it harder to screw up _by accident_ (I'm all for
 getting rid of the delete keyword to make deleting GC-allocated memory less
 obvious), but if you insist on doing something, the language should assume you
 have a good reason.  Deleting GC allocated memory is an advanced technique that
 you do at your own risk, just like using C's malloc/free, using pointers,
turning
 off bounds checking, casting, using inline assembly, not initializing
variables at
 declaration, and all the other dangerous but sometimes useful stuff that "safe"
 languages won't let you do.  Disabling this wouldn't absolutely prevent
corruption
 of the GC heap since D still has pointers, which in principle could be made to
 point anywhere, and since one could still do something stupid like store the
only
 pointer to an object in a region not scanned by the GC via casting or
something.
 The bottom line is that I fail to see how deleting GC-allocated memory is any
 different than other dangerous but useful things that can be done in D, and
unless
 we want to make D a completely safe language at the expense of the flexibility
 that its unsafeness offers, I don't understand why you're so opposed to it.
Because you have malloc. If you didn't, I'd be all with you. Just use malloc and leave the GC heap alone. It's the perfect setup. Andrei
Feb 09 2009
prev sibling parent grauzone <none example.net> writes:
Andrei Alexandrescu wrote:
 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
Using malloc would probably force us to overwrite a class' new operator. Then you no longer had the choice between automatic and manual freeing, because the new operator implementation is fixed. Basically, using malloc would add a load of crap to the program. Having delete (or a function like GC.delete()) is a so much simpler and nicer solution. Why would you remove that?
Feb 10 2009
prev sibling next sibling parent reply Ary Borenszweig <ary esperanto.org.ar> writes:
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(); }
Feb 09 2009
next sibling parent reply Michel Fortin <michel.fortin michelf.com> writes:
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. Another question is what happens when X is an abstract class. -- Michel Fortin michel.fortin michelf.com http://michelf.com/
Feb 09 2009
parent reply Christopher Wright <dhasenan gmail.com> writes:
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.
Feb 09 2009
parent reply Denis Koroskin <2korden gmail.com> writes:
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
Feb 09 2009
parent Christopher Wright <dhasenan gmail.com> writes:
Denis Koroskin wrote:
 Christopher Wright Wrote:
 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
Okay, so your proposal relies on the nullable types proposal, which I dislike; and given the nullable types proposal, this is reasonable.
Feb 11 2009
prev sibling next sibling parent Don <nospam nospam.com> writes:
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(); }
That's interesting, because even there, you don't want X to be a nullable type. You don't want x to be readable AT ALL until one of the constructor calls has happened. In fact, there's already a syntax for this. The solution is: X x = void; if(someCondition) { x = new SomeX(); } else { x = new SomeOtherX(); }
Feb 09 2009
prev sibling next sibling parent reply Daniel Keep <daniel.keep.lists gmail.com> writes:
Ary Borenszweig wrote:
 How would you do this?
 
 X x;
 
 if(someCondition) {
   x = new SomeX();
 } else {
   x =  new SomeOtherX();
 }
One option is the "turn everything into an expression" route. This is 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. -- Daniel
Feb 09 2009
parent reply Frits van Bommel <fvbommel REMwOVExCAPSs.nl> writes:
Daniel Keep wrote:
 One option is the "turn everything into an expression" route.  This is

 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...
Feb 09 2009
parent Daniel Keep <daniel.keep.lists gmail.com> writes:
Frits van Bommel wrote:
 Daniel Keep wrote:
 One option is the "turn everything into an expression" route.  This is

 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
Feb 09 2009
prev sibling next sibling parent reply "Denis Koroskin" <2korden gmail.com> writes:
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(); } [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] 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?
Feb 09 2009
parent "Denis Koroskin" <2korden gmail.com> writes:
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.
Feb 09 2009
prev sibling parent Alex Burton <alexibu mac.com> writes:
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
Feb 09 2009
prev sibling next sibling parent reply Daniel Keep <daniel.keep.lists gmail.com> writes:
Alex Burton wrote:
 I think it makes no sense to have nullable pointers in a high level language
like D.
Oh, and how do you intend to make linked lists? Or trees? Or any non-trivial data structure? Saying they have no place in a language is just silly; the question should be whether they should be the default or not (I would contend that they shouldn't be.)
 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);
Can I just say that I *LOATHE* this syntax? It's one thing I always despised about C++; it simply makes NO sense whatsoever. It looks like the bastard offspring of a function declaration and a function call. The first *is* redundant, but then I wouldn't state X twice, I'd use "auto" (because types are almost never a single letter anyway.) Add to that that the existing syntax is much more intuitive; it even reads like a sentence.
 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)
I made a proposal quite some time ago detailing how we could have not only scope members of classes, but even scope RETURN values, but I think it got maybe one reply... :P Given that one of D's tenets is to make it simpler to write deterministic, bullet-proof code, the complete lack of deterministic destruction of classes has always felt like a huge hole in the language.
 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;
 Vector2 v = new Vector2();
What is Vector2? ... have pointers, so this isn't an issue. (Note that I don't have any idea how you would heap-allocate a value
 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.
Passing garbage to a function doesn't have any bearing on the non-nullable discussion. If you're casting random integers to pointers and passing them to functions, you reap what you sow. :) That said, passing null is more like omitting an ingredient. Which is unfortunate if you're making soup and the ingredient is "water."
 There are much better, and less error prone ways to write code in a high level
language than allowing null pointers.
 
 Alex
While I'm a strong believer in non-nullable types, I don't think null pointers are evil. They're a tool like anything else; problems only arise when you misue them. I believe they're the wrong default choice, because they don't come up in normal code, but you've got to fall over yourself to make sure they don't become an issue. Let me chip in a practical example that happened just yesterday. contracts, so I'm making judicious use of Debug.Assert calls to ensure that object references are non-null. And surprise, surprise, my program crashes with null dereference errors. I walk the stack trace back, only to discover that the null got stored at some point. So the program doesn't even crash when the problem occurs (storing a null reference in a field that shouldn't have one) it occurs later in the execution when I'm no longer able to easily determine what went wrong. Eventually, I tracked it down to one of the few places in the code that I forgot to assert the reference was non-null. It was null because Microsoft didn't bother to document that a particular "magic object" doesn't exist until a certain point. -- Daniel
Feb 09 2009
parent reply "Nick Sabalausky" <a a.a> writes:
"Daniel Keep" <daniel.keep.lists gmail.com> wrote in message 
news:gmpd71$8uj$1 digitalmars.com...
 Alex Burton wrote:
 I think it makes no sense to have nullable pointers in a high level 
 language like D.
Oh, and how do you intend to make linked lists? Or trees? Or any non-trivial data structure?
Null Object Pattern: -------------- class LinkedListNode(T) { LinkedListNode!(T) next; private static LinkedListNode!(T) _end; static LinkedListNode!(T) end() { return _end; } static this() { _end = new LinkedListNode!(T); } } -------------- However, I admit this is little more than re-inventing null. But it's at least possible.
 Saying they have no place in a language is just silly; the question
 should be whether they should be the default or not (I would contend
 that they shouldn't be.)
Agreed.
 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);
Can I just say that I *LOATHE* this syntax? It's one thing I always despised about C++; it simply makes NO sense whatsoever. It looks like the bastard offspring of a function declaration and a function call. The first *is* redundant, but then I wouldn't state X twice, I'd use "auto" (because types are almost never a single letter anyway.) Add to that that the existing syntax is much more intuitive; it even reads like a sentence.
Just a thought: Would you hate this less: X(32) x;
 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)
I made a proposal quite some time ago detailing how we could have not only scope members of classes, but even scope RETURN values, but I think it got maybe one reply... :P Given that one of D's tenets is to make it simpler to write deterministic, bullet-proof code, the complete lack of deterministic destruction of classes has always felt like a huge hole in the language.
I don't suppose you have a link or the subject line of that handy?
 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.
Passing garbage to a function doesn't have any bearing on the non-nullable discussion. If you're casting random integers to pointers and passing them to functions, you reap what you sow. :) That said, passing null is more like omitting an ingredient. Which is unfortunate if you're making soup and the ingredient is "water."
I have to say, I'm loving these analogies :-)
 While I'm a strong believer in non-nullable types, I don't think null
 pointers are evil.  They're a tool like anything else; problems only
 arise when you misue them.

 I believe they're the wrong default choice, because they don't come up
 in normal code, but you've got to fall over yourself to make sure they
 don't become an issue.
That's pretty much my opinion on the matter as well. Null Object Pattern is a usable alternative to nullables, and even more powerful (the "null" is capable of functions, data and polymorphism), but in the cases when you don't need that extra power, a non-default nullable is just less work and isn't really any less safe.
 Eventually, I tracked it down to one of the few places in the code that
 I forgot to assert the reference was non-null.  It was null because
 Microsoft didn't bother to document that a particular "magic object"
 doesn't exist until a certain point.
pulling my hair out figuring out a particular bug, and stepping through in the debugger only perplexed me more. Turned out there was an object with a property I was setting, but the new value was somehow "queued" and didn't actually take until later on. The docs hadn't mentioned that particular behavior.
Feb 09 2009
next sibling parent reply Daniel Keep <daniel.keep.lists gmail.com> writes:
Nick Sabalausky wrote:
 "Daniel Keep" <daniel.keep.lists gmail.com> wrote in message 
 news:gmpd71$8uj$1 digitalmars.com...
 Alex Burton wrote:
 I think it makes no sense to have nullable pointers in a high level 
 language like D.
Oh, and how do you intend to make linked lists? Or trees? Or any non-trivial data structure?
Null Object Pattern: -------------- class LinkedListNode(T) { LinkedListNode!(T) next; private static LinkedListNode!(T) _end; static LinkedListNode!(T) end() { return _end; } static this() { _end = new LinkedListNode!(T); } } -------------- However, I admit this is little more than re-inventing null. But it's at least possible.
And don't forget that in doing this, you've lost the benefit of dereferencing a null crashing the program: if you accidentally access the _end member, you won't notice unless you explicitly check for it.
 [snip]
 In D :

 ... we can write X x(32); instead of X x = new
 X(32);
Can I just say that I *LOATHE* this syntax? It's one thing I always despised about C++; it simply makes NO sense whatsoever. It looks like the bastard offspring of a function declaration and a function call. ...
Just a thought: Would you hate this less: X(32) x;
That's just as bad; it looks like a parameterised type where you forgot the '!'.
 I made a proposal quite some time ago detailing how we could have not
 only scope members of classes, but even scope RETURN values, but I think
 it got maybe one reply... :P

 Given that one of D's tenets is to make it simpler to write
 deterministic, bullet-proof code, the complete lack of deterministic
 destruction of classes has always felt like a huge hole in the language.
I don't suppose you have a link or the subject line of that handy?
Took me a while to find it, even knowing what it was about. Ended up having to manually go through the digitalmars.com archives. :P http://www.digitalmars.com/d/archives/digitalmars/D/38329.html It's probably somewhat anachronistic now. For anyone who isn't aware, back in the old days, 'scope' was 'auto' whilst 'auto' was 'auto.' It sort of did two things and was a bit confusing. Just replace 'auto' with 'scope' and you should be fine. :P Also, back then auto didn't stack-allocate. If I may digress to the land of make-believe for a moment: if this proposal was implemented, you could get around the whole "stack allocation" thing like this: { scope X foo() { return new X; } scope X x = foo(); } Which could become this: { void foo(ubyte[] storage, inout X ptr) { static if( X.sizeofinstance > storage.length ) { ptr = new X; } else { storage[0..X.sizeofinstance] = cast(ubyte[])(new X); } } ubyte[X.sizeofinstance] x_storage; scope X x = x_storage.ptr; foo(x_storage, x); } The caller allocates enough space for an instance of X and passes both the storage and the actual reference variable. The caller can then either store the instance in that array, or make the reference point to somewhere else. Just a thought. :P </digression>
 [snip]
 While I'm a strong believer in non-nullable types, I don't think null
 pointers are evil.  They're a tool like anything else; problems only
 arise when you misue them.

 I believe they're the wrong default choice, because they don't come up
 in normal code, but you've got to fall over yourself to make sure they
 don't become an issue.
That's pretty much my opinion on the matter as well. Null Object Pattern is a usable alternative to nullables, and even more powerful (the "null" is capable of functions, data and polymorphism), but in the cases when you don't need that extra power, a non-default nullable is just less work and isn't really any less safe.
I'd argue it's more safe because it'll crash your program faster. :D
 Eventually, I tracked it down to one of the few places in the code that
 I forgot to assert the reference was non-null.  It was null because
 Microsoft didn't bother to document that a particular "magic object"
 doesn't exist until a certain point.
pulling my hair out figuring out a particular bug, and stepping through in the debugger only perplexed me more. Turned out there was an object with a property I was setting, but the new value was somehow "queued" and didn't actually take until later on. The docs hadn't mentioned that particular behavior.
Microsoft just seems to be really bad at documentation. They go to all that effort of creating a tremendous amount of documentation, but don't succeed in actually saying anything. My favourites are the innumerable classes in .NET or XNA that document every single member function and property... but fail to state what any of them ACTUALLY DO. Or what the arguments are for. Or what the valid values are. Or under what circumstances it's usable. Or how to even create the bloody things. -- Daniel
Feb 10 2009
parent "Nick Sabalausky" <a a.a> writes:
"Daniel Keep" <daniel.keep.lists gmail.com> wrote in message 
news:gmrfe8$10l4$1 digitalmars.com...
 Nick Sabalausky wrote:
 "Daniel Keep" <daniel.keep.lists gmail.com> wrote in message
 news:gmpd71$8uj$1 digitalmars.com...
Took me a while to find it, even knowing what it was about. Ended up having to manually go through the digitalmars.com archives. :P http://www.digitalmars.com/d/archives/digitalmars/D/38329.html It's probably somewhat anachronistic now. For anyone who isn't aware, back in the old days, 'scope' was 'auto' whilst 'auto' was 'auto.' It sort of did two things and was a bit confusing. Just replace 'auto' with 'scope' and you should be fine. :P
Interesting. I'd love to see this get some more attention and discussion simply because what you had said earlier is also one of my own pet peeves about D, and I couldn't have descrbed it any better than you did:
 Given that one of D's tenets is to make it simpler to write
 deterministic, bullet-proof code, the complete lack of deterministic
 destruction of classes has always felt like a huge hole in the language.
Feb 10 2009
prev sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Nick Sabalausky wrote:
 "Daniel Keep" <daniel.keep.lists gmail.com> wrote in message 
 news:gmpd71$8uj$1 digitalmars.com...
 Alex Burton wrote:
 I think it makes no sense to have nullable pointers in a high level 
 language like D.
Oh, and how do you intend to make linked lists? Or trees? Or any non-trivial data structure?
Null Object Pattern: -------------- class LinkedListNode(T) { LinkedListNode!(T) next; private static LinkedListNode!(T) _end; static LinkedListNode!(T) end() { return _end; } static this() { _end = new LinkedListNode!(T); } } --------------
What is "next" for the _end node? Andrei
Feb 10 2009
parent reply "Denis Koroskin" <2korden gmail.com> writes:
On Tue, 10 Feb 2009 17:11:06 +0300, Andrei Alexandrescu  
<SeeWebsiteForEmail erdani.org> wrote:

 Nick Sabalausky wrote:
 "Daniel Keep" <daniel.keep.lists gmail.com> wrote in message  
 news:gmpd71$8uj$1 digitalmars.com...
 Alex Burton wrote:
 I think it makes no sense to have nullable pointers in a high level  
 language like D.
Oh, and how do you intend to make linked lists? Or trees? Or any non-trivial data structure?
Null Object Pattern: -------------- class LinkedListNode(T) { LinkedListNode!(T) next; private static LinkedListNode!(T) _end; static LinkedListNode!(T) end() { return _end; } static this() { _end = new LinkedListNode!(T); } } --------------
What is "next" for the _end node? Andrei
Perhaps, 'this' (i.e. itself).
Feb 10 2009
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Denis Koroskin wrote:
 On Tue, 10 Feb 2009 17:11:06 +0300, Andrei Alexandrescu 
 <SeeWebsiteForEmail erdani.org> wrote:
 
 Nick Sabalausky wrote:
 "Daniel Keep" <daniel.keep.lists gmail.com> wrote in message 
 news:gmpd71$8uj$1 digitalmars.com...
 Alex Burton wrote:
 I think it makes no sense to have nullable pointers in a high level 
 language like D.
Oh, and how do you intend to make linked lists? Or trees? Or any non-trivial data structure?
Null Object Pattern: -------------- class LinkedListNode(T) { LinkedListNode!(T) next; private static LinkedListNode!(T) _end; static LinkedListNode!(T) end() { return _end; } static this() { _end = new LinkedListNode!(T); } } --------------
What is "next" for the _end node? Andrei
Perhaps, 'this' (i.e. itself).
Then there's going to be quite a few infinite loops out there... Andrei
Feb 10 2009
parent reply "Denis Koroskin" <2korden gmail.com> writes:
On Tue, 10 Feb 2009 17:40:36 +0300, Andrei Alexandrescu  
<SeeWebsiteForEmail erdani.org> wrote:

 Denis Koroskin wrote:
 On Tue, 10 Feb 2009 17:11:06 +0300, Andrei Alexandrescu  
 <SeeWebsiteForEmail erdani.org> wrote:

 Nick Sabalausky wrote:
 "Daniel Keep" <daniel.keep.lists gmail.com> wrote in message  
 news:gmpd71$8uj$1 digitalmars.com...
 Alex Burton wrote:
 I think it makes no sense to have nullable pointers in a high level  
 language like D.
Oh, and how do you intend to make linked lists? Or trees? Or any non-trivial data structure?
Null Object Pattern: -------------- class LinkedListNode(T) { LinkedListNode!(T) next; private static LinkedListNode!(T) _end; static LinkedListNode!(T) end() { return _end; } static this() { _end = new LinkedListNode!(T); } } --------------
What is "next" for the _end node? Andrei
Perhaps, 'this' (i.e. itself).
Then there's going to be quite a few infinite loops out there... Andrei
No, you just need to have a private ctor: class LinkedListNode(T) { private enum Foo { Bar, } private this(Foo foo) // not accessible outside of the module { _next = this; } static this() { _end = new LinkedListNode!(T)(Foo.Bar); } private LinkedListNode _next; }
Feb 10 2009
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Denis Koroskin wrote:
 On Tue, 10 Feb 2009 17:40:36 +0300, Andrei Alexandrescu 
 <SeeWebsiteForEmail erdani.org> wrote:
 
 Denis Koroskin wrote:
 On Tue, 10 Feb 2009 17:11:06 +0300, Andrei Alexandrescu 
 <SeeWebsiteForEmail erdani.org> wrote:

 Nick Sabalausky wrote:
 "Daniel Keep" <daniel.keep.lists gmail.com> wrote in message 
 news:gmpd71$8uj$1 digitalmars.com...
 Alex Burton wrote:
 I think it makes no sense to have nullable pointers in a high 
 level language like D.
Oh, and how do you intend to make linked lists? Or trees? Or any non-trivial data structure?
Null Object Pattern: -------------- class LinkedListNode(T) { LinkedListNode!(T) next; private static LinkedListNode!(T) _end; static LinkedListNode!(T) end() { return _end; } static this() { _end = new LinkedListNode!(T); } } --------------
What is "next" for the _end node? Andrei
Perhaps, 'this' (i.e. itself).
Then there's going to be quite a few infinite loops out there... Andrei
No, you just need to have a private ctor:
I meant that code that fails to check for _end when iterating through a list will never stop. Andrei
Feb 10 2009
parent reply "Denis Koroskin" <2korden gmail.com> writes:
On Tue, 10 Feb 2009 19:41:25 +0300, Andrei Alexandrescu  
<SeeWebsiteForEmail erdani.org> wrote:

 Denis Koroskin wrote:
 On Tue, 10 Feb 2009 17:40:36 +0300, Andrei Alexandrescu  
 <SeeWebsiteForEmail erdani.org> wrote:

 Denis Koroskin wrote:
 On Tue, 10 Feb 2009 17:11:06 +0300, Andrei Alexandrescu  
 <SeeWebsiteForEmail erdani.org> wrote:

 Nick Sabalausky wrote:
 "Daniel Keep" <daniel.keep.lists gmail.com> wrote in message  
 news:gmpd71$8uj$1 digitalmars.com...
 Alex Burton wrote:
 I think it makes no sense to have nullable pointers in a high  
 level language like D.
Oh, and how do you intend to make linked lists? Or trees? Or any non-trivial data structure?
Null Object Pattern: -------------- class LinkedListNode(T) { LinkedListNode!(T) next; private static LinkedListNode!(T) _end; static LinkedListNode!(T) end() { return _end; } static this() { _end = new LinkedListNode!(T); } } --------------
What is "next" for the _end node? Andrei
Perhaps, 'this' (i.e. itself).
Then there's going to be quite a few infinite loops out there... Andrei
No, you just need to have a private ctor:
I meant that code that fails to check for _end when iterating through a list will never stop. Andrei
No, error like that will be detected at compile time (comparison against null is disallowed for non-nullable reference).
Feb 10 2009
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Denis Koroskin wrote:
 On Tue, 10 Feb 2009 19:41:25 +0300, Andrei Alexandrescu 
 <SeeWebsiteForEmail erdani.org> wrote:
 
 Denis Koroskin wrote:
 On Tue, 10 Feb 2009 17:40:36 +0300, Andrei Alexandrescu 
 <SeeWebsiteForEmail erdani.org> wrote:

 Denis Koroskin wrote:
 On Tue, 10 Feb 2009 17:11:06 +0300, Andrei Alexandrescu 
 <SeeWebsiteForEmail erdani.org> wrote:

 Nick Sabalausky wrote:
 "Daniel Keep" <daniel.keep.lists gmail.com> wrote in message 
 news:gmpd71$8uj$1 digitalmars.com...
 Alex Burton wrote:
 I think it makes no sense to have nullable pointers in a high 
 level language like D.
Oh, and how do you intend to make linked lists? Or trees? Or any non-trivial data structure?
Null Object Pattern: -------------- class LinkedListNode(T) { LinkedListNode!(T) next; private static LinkedListNode!(T) _end; static LinkedListNode!(T) end() { return _end; } static this() { _end = new LinkedListNode!(T); } } --------------
What is "next" for the _end node? Andrei
Perhaps, 'this' (i.e. itself).
Then there's going to be quite a few infinite loops out there... Andrei
No, you just need to have a private ctor:
I meant that code that fails to check for _end when iterating through a list will never stop. Andrei
No, error like that will be detected at compile time (comparison against null is disallowed for non-nullable reference).
I must be misunderstanding something. At the highest level, the example given replaced the terminating node of a list (which traditionally is null) with a node that had itself as the next node. This means that if you iterate such a list without testing for node == _end, you will have an infinite loop. This has nothing to do with types. Andrei
Feb 10 2009
parent reply "Nick Sabalausky" <a a.a> writes:
"Andrei Alexandrescu" <SeeWebsiteForEmail erdani.org> wrote in message 
news:gmsd6o$2h77$1 digitalmars.com...
 I must be misunderstanding something. At the highest level, the example 
 given replaced the terminating node of a list (which traditionally is 
 null) with a node that had itself as the next node.

 This means that if you iterate such a list without testing for node == 
 _end, you will have an infinite loop. This has nothing to do with types.
------------------ class LinkedListNode(T) { static this() { _end = new LinkedListNode!(T)(55555); } this() { // Prevent _next from being auto-inited to // a new LinkedListNode, which in turn // would have a _next auto-inited to a // new LinkedListNode, etc. _next = _end; } // Prevent _end._next from being auto-inited to // a new LinkedListNode, which in turn // would have a _next auto-inited to a // new LinkedListNode, etc. private this(int dummy) { _next = this; } private static LinkedListNode!(T) _end; static LinkedListNode!(T) end() { return _end; } private LinkedListNode!(T) _next; void next(LinkedListNode!(T) newNext) { _next = newNext; } LinkedListNode!(T) next() { // Prevent infinite iteration if(this is _end) throw new Exception("Iterated past the end!"); else return _next; } } ------------------ Note, however, that I'm not advocating this approach to linked lists or other advanced structures (since it's so much uglier than just using a nullable reference). I'm just pointing out that it's at least *possible* without using nullable references.
Feb 10 2009
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Nick Sabalausky wrote:
 "Andrei Alexandrescu" <SeeWebsiteForEmail erdani.org> wrote in message 
 news:gmsd6o$2h77$1 digitalmars.com...
 I must be misunderstanding something. At the highest level, the example 
 given replaced the terminating node of a list (which traditionally is 
 null) with a node that had itself as the next node.

 This means that if you iterate such a list without testing for node == 
 _end, you will have an infinite loop. This has nothing to do with types.
------------------ class LinkedListNode(T)
[snip] Oh ok, now I understand. Thanks. Andrei
Feb 10 2009
prev sibling next sibling parent reply grauzone <none example.net> writes:
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;
Add this code to your class X: static X opCall(T...)(T x) { return new X(x); } Then you can write:
 auto x = X();
Can it get any less redundant? The opCall is a bit annoying, but needs to be written only once.
 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;
For structs, use opCall() to initialize them. opCall is the best way to initialize a struct, because struct literal are totally utterly useless. If you use opCall to initialize classes as well (see above), then the syntax for initializing structs and classes will be exactly the same.
 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.
Like what?
 Alex
Feb 09 2009
parent "Nick Sabalausky" <a a.a> writes:
"grauzone" <none example.net> wrote in message 
news:gmpe6g$adf$2 digitalmars.com...
 Alex Burton wrote:

 For structs, use opCall() to initialize them. opCall is the best way to 
 initialize a struct, because struct literal are totally utterly useless.

 If you use opCall to initialize classes as well (see above), then the 
 syntax for initializing structs and classes will be exactly the same.
That's good advice for using D today, but it's still subject to human error. Making "X x;" serve as an implicit "X x = new X();" for classes would eliminate that human error.
Feb 09 2009
prev sibling next sibling parent Alex Burton <alexibu mac.com> writes:
Daniel Keep Wrote:

 
 
 Alex Burton wrote:
 I think it makes no sense to have nullable pointers in a high level language
like D.
Oh, and how do you intend to make linked lists? Or trees? Or any non-trivial data structure?
I am not saying than null pointers should be barred - I don't think it would be possible. To implement the above, in a tree you will probably have arrays of pointers, each element of the array should be a valid pointer, and never null. To implement a linked list, I agree that a null pointer is a good implementation. You could still have the X * x = 0; pointer syntax for implementing special low level stuff like this. For the majority of uses allowing null references is not necessary.
 
 Saying they have no place in a language is just silly; the question
 should be whether they should be the default or not (I would contend
 that they shouldn't be.)
 
 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);
Can I just say that I *LOATHE* this syntax? It's one thing I always despised about C++; it simply makes NO sense whatsoever. It looks like the bastard offspring of a function declaration and a function call.
Does that mean you loathe it in D now for structs too or just if it was implemented for classes ?
 The first *is* redundant, but then I wouldn't state X twice, I'd use
 "auto" (because types are almost never a single letter anyway.)  Add to
 that that the existing syntax is much more intuitive; it even reads like
 a sentence.
I think that is personal taste. The sentance starting with the word auto doesn't make much sense to me.
 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.
Passing garbage to a function doesn't have any bearing on the non-nullable discussion. If you're casting random integers to pointers and passing them to functions, you reap what you sow. :)
My point was that 0 is just as much not a pointer to an instance of X as 0x012456 is (unless you are quite lucky)
 
 That said, passing null is more like omitting an ingredient.  Which is
 unfortunate if you're making soup and the ingredient is "water."
 
 There are much better, and less error prone ways to write code in a high level
language than allowing null pointers.
 
 Alex
While I'm a strong believer in non-nullable types, I don't think null pointers are evil. They're a tool like anything else; problems only arise when you misue them. I believe they're the wrong default choice, because they don't come up in normal code, but you've got to fall over yourself to make sure they don't become an issue. Let me chip in a practical example that happened just yesterday. contracts, so I'm making judicious use of Debug.Assert calls to ensure that object references are non-null. And surprise, surprise, my program crashes with null dereference errors. I walk the stack trace back, only to discover that the null got stored at some point. So the program doesn't even crash when the problem occurs (storing a null reference in a field that shouldn't have one) it occurs later in the execution when I'm no longer able to easily determine what went wrong. Eventually, I tracked it down to one of the few places in the code that I forgot to assert the reference was non-null. It was null because Microsoft didn't bother to document that a particular "magic object" doesn't exist until a certain point.
Doesn't this prove my point ? You are littering code with Debug.Assert statements to check the assumption that pointers are non null. It is much easier to manage code where you can safely assume that all pointers point to something. If you really want to write some small subset of code with a nullable type write a little template smart pointer for it, that forces you to explicitly check if it is null before using it. Like this: struct Nullable<T> //sorry don't know D syntax for this { private: T value; public this() { value = 0; } this(T val) { value = val; } T get() { if (value is null) throw NullableTypeException("value is null") else return value; } bool isnull() { return value is null; } }; void f(nullable!X x) { x.makeACupOfTea(); //wont compile. x.get().makeACupOfTea(); //will throw if null if (!x.isnull()) x.get().makeACupOfTea(); //proper use of nullable type } Sorry about the syntax - I havent done templates in D yet (probably this says a lot about the improvements over C++). Alex
Feb 09 2009
prev sibling parent reply Alex Burton <alexibu mac.com> writes:
Daniel Keep Wrote:

 
 
 Alex Burton wrote:
 I think it makes no sense to have nullable pointers in a high level language
like D.
Oh, and how do you intend to make linked lists? Or trees? Or any non-trivial data structure?
I am not saying than null pointers should be barred - I don't think it would be possible. To implement the above, in a tree you will probably have arrays of pointers, each element of the array should be a valid pointer, and never null. To implement a linked list, I agree that a null pointer is a good implementation. You could still have the X * x = 0; pointer syntax for implementing special low level stuff like this. For the majority of uses allowing null references is not necessary.
 
 Saying they have no place in a language is just silly; the question
 should be whether they should be the default or not (I would contend
 that they shouldn't be.)
 
 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);
Can I just say that I *LOATHE* this syntax? It's one thing I always despised about C++; it simply makes NO sense whatsoever. It looks like the bastard offspring of a function declaration and a function call.
Does that mean you loathe it in D now for structs too or just if it was implemented for classes ?
 The first *is* redundant, but then I wouldn't state X twice, I'd use
 "auto" (because types are almost never a single letter anyway.)  Add to
 that that the existing syntax is much more intuitive; it even reads like
 a sentence.
I think that is personal taste. The sentance starting with the word auto doesn't make much sense to me.
 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.
Passing garbage to a function doesn't have any bearing on the non-nullable discussion. If you're casting random integers to pointers and passing them to functions, you reap what you sow. :)
My point was that 0 is just as much not a pointer to an instance of X as 0x012456 is (unless you are quite lucky :))
 
 That said, passing null is more like omitting an ingredient.  Which is
 unfortunate if you're making soup and the ingredient is "water."
 
 There are much better, and less error prone ways to write code in a high level
language than allowing null pointers.
 
 Alex
While I'm a strong believer in non-nullable types, I don't think null pointers are evil. They're a tool like anything else; problems only arise when you misue them. I believe they're the wrong default choice, because they don't come up in normal code, but you've got to fall over yourself to make sure they don't become an issue. Let me chip in a practical example that happened just yesterday. contracts, so I'm making judicious use of Debug.Assert calls to ensure that object references are non-null. And surprise, surprise, my program crashes with null dereference errors. I walk the stack trace back, only to discover that the null got stored at some point. So the program doesn't even crash when the problem occurs (storing a null reference in a field that shouldn't have one) it occurs later in the execution when I'm no longer able to easily determine what went wrong. Eventually, I tracked it down to one of the few places in the code that I forgot to assert the reference was non-null. It was null because Microsoft didn't bother to document that a particular "magic object" doesn't exist until a certain point.
Doesn't this prove my point ? You are littering code with Debug.Assert statements to check the assumption that pointers are non null. It is much easier to manage code where you can safely assume that all pointers point to something, which is one of the great benefits about the garbage collector. If you really want to write some small subset of code with a nullable type then I suggest write a little template smart pointer for it, that forces you to explicitly check if it is null before using it. Like this: struct Nullable<T> //sorry don't know D syntax for this { private: T * value; public this() { value = 0; } this(T val) { value = val; } T get() { if (value is null) throw NullableTypeException("value is null") else return value; } bool isnull() { return value is null; } }; void f(nullable!X x) { x.makeACupOfTea(); //wont compile. x.get().makeACupOfTea(); //will throw if null if (!x.isnull()) x.get().makeACupOfTea(); //proper use of nullable type } Sorry about the syntax - I havent done templates in D yet (probably this says a lot about the improvements over C++). Alex
Feb 09 2009
parent reply Daniel Keep <daniel.keep.lists gmail.com> writes:
Alex Burton wrote:
 Daniel Keep Wrote:
 
 Alex Burton wrote:
 I think it makes no sense to have nullable pointers in a high level language
like D.
Oh, and how do you intend to make linked lists? Or trees? Or any non-trivial data structure?
I am not saying than null pointers should be barred - I don't think it would be possible. To implement the above, in a tree you will probably have arrays of pointers, each element of the array should be a valid pointer, and never null. To implement a linked list, I agree that a null pointer is a good implementation. You could still have the X * x = 0; pointer syntax for implementing special low level stuff like this. For the majority of uses allowing null references is not necessary.
So you're suggesting dynamically allocating a dynamically sized array for each node in, say, a binary tree. I really hope you never run into my data structures and algorithms lecturer... I can't imagine that idea would go over well. :P Incidentally, "X* x = 0;" doesn't work in D. Only "X* x = null;" will work, since 0 is an integer literal, not a pointer literal...
 Saying they have no place in a language is just silly; the question
 should be whether they should be the default or not (I would contend
 that they shouldn't be.)
You didn't respond to this comment; the bit about using arrays in a tree above says to me that you want to push null as far out of the language as technically feasible, no matter the cost. Nullable references are a tool. I'm agreeing with you on the idea that it's the WRONG tool by default, but that doesn't mean we need to demonise it. It is useful in cases where it's appropriate, and would be nigh impossible to replace.
 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);
Can I just say that I *LOATHE* this syntax? It's one thing I always despised about C++; it simply makes NO sense whatsoever. It looks like the bastard offspring of a function declaration and a function call.
Does that mean you loathe it in D now for structs too or just if it was implemented for classes ?
I loathe this:
 X x(32);
Maybe it's a matter of being used to it, but there is no way I can look at that and say "yup, looks like initialisation." After all, it's only one token away from this:
 x(32);
And that's a function call. Or one token away from this:
 X.x(32);
It just looks like too many other completely different things to be able to fit in the "initialisation"-shaped hole I have in my head. For reference, when I see
 X x;
I mentally rewrite that to:
 X x = X.init;
No assignment, no initialisation.
 The first *is* redundant, but then I wouldn't state X twice, I'd use
 "auto" (because types are almost never a single letter anyway.)  Add to
 that that the existing syntax is much more intuitive; it even reads like
 a sentence.
I think that is personal taste. The sentance starting with the word auto doesn't make much sense to me.
Not entirely; your example did contain redundancy, but only because you didn't use 'auto'. I'm demonstrating that in D, saying 'X x = new X;' is redundant is an invalid argument because you CAN omit the type, at which point there is no redundancy. But there is a level of personal preference at play here, I admit. I don't like "X x(32);" and you don't like "auto"... I guess we're evenly matched. :)
 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.
Passing garbage to a function doesn't have any bearing on the non-nullable discussion. If you're casting random integers to pointers and passing them to functions, you reap what you sow. :)
My point was that 0 is just as much not a pointer to an instance of X as 0x012456 is (unless you are quite lucky :))
 That said, passing null is more like omitting an ingredient.  Which is
 unfortunate if you're making soup and the ingredient is "water."
What I was saying was that sometimes the concept of "nothing" is useful; FAR more useful than the concept of "bicycle", by which I mean "random garbage." I can't think of any situation where passing a random pointer is useful, but plenty of cases where passing "nothing" is.
 There are much better, and less error prone ways to write code in a high level
language than allowing null pointers.

 Alex
While I'm a strong believer in non-nullable types, I don't think null pointers are evil. They're a tool like anything else; problems only arise when you misue them. I believe they're the wrong default choice, because they don't come up in normal code, but you've got to fall over yourself to make sure they don't become an issue. Let me chip in a practical example that happened just yesterday. contracts, so I'm making judicious use of Debug.Assert calls to ensure that object references are non-null. And surprise, surprise, my program crashes with null dereference errors. I walk the stack trace back, only to discover that the null got stored at some point. So the program doesn't even crash when the problem occurs (storing a null reference in a field that shouldn't have one) it occurs later in the execution when I'm no longer able to easily determine what went wrong. Eventually, I tracked it down to one of the few places in the code that I forgot to assert the reference was non-null. It was null because Microsoft didn't bother to document that a particular "magic object" doesn't exist until a certain point.
Doesn't this prove my point ?
You may not have noticed, but:
 While **I'm a strong believer in non-nullable types**, I don't think
 null pointers are evil.  They're a tool like anything else; problems
 only arise when you misue them.
I'm *agreeing* with you that non-nullable should be default. What I was arguing against was the apparent suggestion to outlaw the null pointer on pain of death. Indeed, the example DOES support the "non-nullable by default" argument, hence why I posted it.
 You are littering code with Debug.Assert statements to check the assumption
that pointers are non null.
 It is much easier to manage code where you can safely assume that all pointers
point to something, which is one of the great benefits about the garbage
collector.
 
 If you really want to write some small subset of code with a nullable type
then I suggest write a little template smart pointer for it, that forces you to
explicitly check if it is null before using it.
 Like this:
 
 [snip]
 
 Sorry about the syntax - I havent done templates in D yet (probably this says
a lot about the improvements over C++).
 
 Alex
How do you propose to write a template implementing nullable types when you aren't ALLOWED to use null? You'd have to start casting around between integers and references, which is arguably worse than just supporting nullable types. Incidentally, you cannot implement non-nullable types in D either, which suggests that we need language-level support for both. Also, FYI, these two lines:
 struct Nullable<T>
 void f(nullable!X x)
Should be:
 struct Nullable(T)
 void f(nullable!(X) x)
Note that I tested that second one with a D 1.x compiler; if it's valid in D 2.x, I'm not aware of it. :) -- Daniel
Feb 09 2009
next sibling parent reply Alex Burton <alexibu mac.com> writes:
Daniel Keep Wrote:

 
 
 Alex Burton wrote:
 Daniel Keep Wrote:
 
 Alex Burton wrote:
 I think it makes no sense to have nullable pointers in a high level language
like D.
Oh, and how do you intend to make linked lists? Or trees? Or any non-trivial data structure?
I am not saying than null pointers should be barred - I don't think it would be possible. To implement the above, in a tree you will probably have arrays of pointers, each element of the array should be a valid pointer, and never null. To implement a linked list, I agree that a null pointer is a good implementation. You could still have the X * x = 0; pointer syntax for implementing special low level stuff like this. For the majority of uses allowing null references is not necessary.
So you're suggesting dynamically allocating a dynamically sized array for each node in, say, a binary tree. I really hope you never run into my data structures and algorithms lecturer... I can't imagine that idea would go over well. :P Incidentally, "X* x = 0;" doesn't work in D. Only "X* x = null;" will work, since 0 is an integer literal, not a pointer literal...
OK the point you make is that using nullable pointers is necessary. I would agree, but say that all the examples you have given are at the library implementation level (code that most D developers probably wont want in what they do day to day). IIRC D already has references like X x; but also pointers like in c which are X * x. I would propose that library writers have to use the X * x pointer notation to do their null pointer low level library implementation stuff, and the rest of us use X x which becomes more difficult (not impossible) to make null.
 Saying they have no place in a language is just silly; the question
 should be whether they should be the default or not (I would contend
 that they shouldn't be.)
You didn't respond to this comment; the bit about using arrays in a tree above says to me that you want to push null as far out of the language as technically feasible, no matter the cost. Nullable references are a tool. I'm agreeing with you on the idea that it's the WRONG tool by default, but that doesn't mean we need to demonise it. It is useful in cases where it's appropriate, and would be nigh impossible to replace.
Not as far as technically feasable, just that it should be opt in, and the simplest to type should be non nullable - the default as you suggest. Sorry no demonisation intended. Merely an intimation in the language that if you are dealing with nullable types you probably know what you are doing, as you are exposing yourself to error prone code.
 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);
Can I just say that I *LOATHE* this syntax? It's one thing I always despised about C++; it simply makes NO sense whatsoever. It looks like the bastard offspring of a function declaration and a function call.
Does that mean you loathe it in D now for structs too or just if it was implemented for classes ?
I loathe this:
 X x(32);
Maybe it's a matter of being used to it, but there is no way I can look at that and say "yup, looks like initialisation." After all, it's only one token away from this:
 x(32);
And that's a function call. Or one token away from this:
 X.x(32);
It just looks like too many other completely different things to be able to fit in the "initialisation"-shaped hole I have in my head. For reference, when I see
 X x;
I mentally rewrite that to:
 X x = X.init;
No assignment, no initialisation.
 The first *is* redundant, but then I wouldn't state X twice, I'd use
 "auto" (because types are almost never a single letter anyway.)  Add to
 that that the existing syntax is much more intuitive; it even reads like
 a sentence.
I think that is personal taste. The sentance starting with the word auto doesn't make much sense to me.
Not entirely; your example did contain redundancy, but only because you didn't use 'auto'. I'm demonstrating that in D, saying 'X x = new X;' is redundant is an invalid argument because you CAN omit the type, at which point there is no redundancy. But there is a level of personal preference at play here, I admit. I don't like "X x(32);" and you don't like "auto"... I guess we're evenly matched. :)
I was just pointing out that if we are looking at english sentances auto doesn't make much sense. I do think that code with auto in it requires more searching to find the actual type of a variable. Not in auto x = new X; but in auto x = factory.create();
 You may not have noticed, but:
 
 While **I'm a strong believer in non-nullable types**, I don't think
 null pointers are evil.  They're a tool like anything else; problems
 only arise when you misue them.
I'm *agreeing* with you that non-nullable should be default. What I was arguing against was the apparent suggestion to outlaw the null pointer on pain of death.
You're right I thought you wanted the default to stay as it is. Must be my comprehension of the double negative "non nullable types" :) No pain or death suggested.
 How do you propose to write a template implementing nullable types when
 you aren't ALLOWED to use null?  You'd have to start casting around
 between integers and references, which is arguably worse than just
 supporting nullable types.
 
In the code I wrote which might be considered for addition to a library. I was using the T * val; notation which I would propose is still available to those that want nullable pointers. Again it's use should be mostly limited to library implementation and wrapping C APIs etc.
 Incidentally, you cannot implement non-nullable types in D either, which
 suggests that we need language-level support for both.
Agreed the X * x = null; syntax is still good.
 Also, FYI, these two lines:
 
 struct Nullable<T>
 void f(nullable!X x)
Should be:
 struct Nullable(T)
 void f(nullable!(X) x)
Thanks !
 Note that I tested that second one with a D 1.x compiler; if it's valid
 in D 2.x, I'm not aware of it.  :)
So the only point of contetion between us is that you wont be happy with the syntax. X x(32); X x("bob"); Even though it is currently consistent with struct initialisation, and would save us all some typing. Alex
Feb 09 2009
parent "Nick Sabalausky" <a a.a> writes:
"Alex Burton" <alexibu mac.com> wrote in message 
news:gmqrec$2va3$1 digitalmars.com...
 I would propose that library writers have to use the X * x pointer 
 notation to do their null pointer low level library implementation stuff, 
 and the rest of us use X x which becomes more difficult (not impossible) 
 to make null.
So are you suggesting this?: X* x; // Ok, can be null X x; // Ok, cannot be null X? x; // Illegal If so, I'd have to strongly disagree. That would force the people who need nullability to muck around with the pointers and the extra level of indirection that implies, which will be far more problematic for them then if they just used a nullable non-pointer: X? x; We need all three.
Feb 09 2009
prev sibling parent "Nick Sabalausky" <a a.a> writes:
"Daniel Keep" <daniel.keep.lists gmail.com> wrote in message 
news:gmqmhk$2mb0$1 digitalmars.com...
 How do you propose to write a template implementing nullable types when
 you aren't ALLOWED to use null?  You'd have to start casting around
 between integers and references, which is arguably worse than just
 supporting nullable types.
Devil's advocate: --------------- struct Nullable(T) { private T value; // Assuming "X x;" means "X x = new X();" private bool _isNull=true; void set(T val) { value = val; _isNull = false; } T get() { if (_isNull) throw NullableTypeException("value is null") else return value; } void makeNull() { _isNull = true; } bool isNull() { return _isNull; } } --------------- But admittedly, it's a kludge and vastly inferior to a nice well-implemented "X? x;" at the language level.
Feb 09 2009