www.digitalmars.com         C & C++   DMDScript  

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

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. I'm throwing together a game prototype. Since this is C#, I don't have 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.

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);

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.

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

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. I'm throwing together a game prototype. Since this is C#, I don't have 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.

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);

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.
 

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