Index » General » References in D (page 12)

October 05, 2012
On Fri, 05 Oct 2012 05:19:13 +0100, Alex Burton <alexibureplacewithzero@gmail.com> wrote:

> On Saturday, 15 September 2012 at 17:51:39 UTC, Jonathan M Davis wrote:
>> On Saturday, September 15, 2012 19:35:44 Alex Rønne Petersen wrote:
>>> Out of curiosity: Why? How often does your code actually accept null as
>>> a valid state of a class reference?
>>
>> I have no idea. I know that it's a non-negligible amount of the time, though
>> it's certainly true that they normally have values. But null is how you
>> indicate that a reference has no value. The same goes for arrays and pointers.
>> Sometimes it's useful to have null and sometimes it's useful to know that a
>> value can't be null. I confess though that I find it very surprising how much
>> some people push for non-nullable references, since I've never really found
>> null to be a problem. Sure, once in a while, you get a null pointer/reference
>> and something blows up, but that's very rare in my experience, so I can't help
>> but think that people who hit issues with null pointers on a regular basis are
>> doing something wrong.
>>
>> - Jonathan M Davis
>
> In my experience this sort of attutide is not workable in projects with more than one developer.

Almost all my work is on projects with multiple developers in C/C++ and making extensive use of null.

> It all works OK if everyone knows the 'rules' about when to check for null and when not to.

As every good C/C++ developer does.  The rule is simple, always check for nulls on input passed to "public" functions/methods.  What you do with internal protected and private functions and methods is up to you (I use assert).

> Telling team members that find bugs caused by your null references that they are doing it wrong and next time should check for null is a poor substitute for having the language define the rules.

Having language defined rules is a nice added /bonus/ it doesn't let you off the hook when it comes to being "null safe" in your code.

> A defensive attitude of checking for null everywhere like I have seen in many C++ projects makes the code ugly.

That's a matter of opinion.  I like to see null checks at the top of a function or method, it makes it far more likely to be safe and it means I can ignore the possibility of null from then on - making the code much cleaner.

R

-- 
Using Opera's revolutionary email client: http://www.opera.com/mail/
October 05, 2012
Regan Heath:

> That's a matter of opinion.  I like to see null checks at the top of a function or method, it makes it far more likely to be safe and it means I can ignore the possibility of null from then on - making the code much cleaner.

Even more clear/short/light is to not need such checks, and take arguments with a light tagging syntax that assures them to be not null.

Compare:

void foo1(C1 c1, C2 c2)
in {
    assert(c1 !is null);
    assert(c2 !is null);
} body {
    ...
}


With:

void foo2(C1@ c1, C2@ c2) {
    ...
}


There the @ suffix means not-nullable, it's a D2-compatible syntax. In a better designed language, you do the opposite, adding ? for nullable reference/pointers, so it becomes (both can't be null):

void foo3(C1 c1, C2 c2) {
    ...
}


Doing this moves the burden of verifying not-nullness out of foo2/foo3. If the argument of foo is given to many functions, you don't have to test c1 and c2 in every function, saving lines of code, space, run-time and avoiding mistakes (and null-related bugs are not uncommon).

To create a not-null variable you have to create it assigning it to something that is not null (this is the most common case), or you have to test it. This test for not-null is similar to the tests inside foo1. But such tests tend to be closer to where problems are.

A well implemented not-null system asks you to test nullables before dereferencing, and keeps track of the nullable/notnull type state inside the if statement clauses, avoiding useless tests (this means that if you test for null a nullable, inside the else clause the state of its type is not-null).

Bye,
bearophile
October 05, 2012
On Friday, 5 October 2012 at 13:57:13 UTC, bearophile wrote:
> void foo1(C1 c1, C2 c2)
> in {
>     assert(c1 !is null);
>     assert(c2 !is null);
> } body {
>     ...
> }

And in public library code, you can't even use assert. You have to throw an error/exception. Runtime checks guaranteed even in release mode.
October 05, 2012
On Friday, October 05, 2012 08:45:31 Alex Burton wrote:
> One is the language designer allowing null to be an acceptable
> value for a pointer to an int.
> As should be blatently obvious that null is not a pointer to an
> int, but for historical reasons inherited from C (when people
> were just happy to get out of assembly language) it has been
> allowed.

You are going to find plenty of people who disagree quite strongly with you. There are times when having a type be non-nullable is very useful, but there are times when having a type be nullable is extremely useful. You seem to think that the idea of nullability is bad in the first place, and while some people will agree with you, a _lot_ will not. You're fighting a losing battle if you're arguing that.

It would be a _huge_ design mistake for a systems language not to have nullable pointers. Having non-nullable references or pointers in addition to nullable ones might be useful, but not having nullable ones at all would be crippling - especially for a systems language.

I think that we're clearly going to have to agree to disagree here.

- Jonathan M Davis
October 05, 2012
On 2012-13-05 20:10, Jonathan M Davis <jmdavisProg@gmx.com> wrote:

> It would be a _huge_ design mistake for a systems language not to have
> nullable pointers. Having non-nullable references or pointers in addition to nullable ones might be useful, but not having nullable ones
> at all would be crippling - especially for a systems language.

Indeed. However, given both types, I would argue that non-nullable by
default would go best with the D design guidelines - safe before unsafe,
to be specific.

Of course, given the current state of D, retroactively fitting
non-nullable references/pointers by default is impossible, unwanted,
and simply a bloody stupid idea.

-- 
Simen
October 05, 2012
On Fri, Oct 5, 2012 at 11:13 AM, Jonathan M Davis <jmdavisProg@gmx.com>wrote:

> You are going to find plenty of people who disagree quite strongly with
> you.
> There are times when having a type be non-nullable is very useful, but
> there
> are times when having a type be nullable is extremely useful. You seem to
> think that the idea of nullability is bad in the first place, and while
> some
> people will agree with you, a _lot_ will not. You're fighting a losing
> battle
> if you're arguing that.
>
> It would be a _huge_ design mistake for a systems language not to have
> nullable pointers. Having non-nullable references or pointers in addition
> to
> nullable ones might be useful, but not having nullable ones at all would be
> crippling - especially for a systems language.
>
> I think that we're clearly going to have to agree to disagree here.
>
> - Jonathan M Davis
>


I do not think he was arguing removing null completely from the type system. It is just that for the vast majority of the cases, references are not meant to be null and thus it should be disallowed by default. If you want to use a nullable reference you have to explicitly ask for such.

--
Ziad


October 05, 2012
On Friday, 5 October 2012 at 18:36:07 UTC, Simen Kjaeraas wrote:
> Indeed. However, given both types, I would argue that non-nullable by
> default would go best with the D design guidelines - safe before unsafe,
> to be specific.
>

Clearly that would be the case, else we're tossing aside the guidlines as they are written. References should be safe first which means they must be non-nullable by default, but it also should be possible to make references unsafe for those who require unsafe abilities (this is also stated in the guidlines). It is that sort of general set of guidlines that convinced me into investing my time in D, and away from C++ which has the opposing set of guidelines. I can do safe coding by default, and also unsafe in the normally few places where required.

> Of course, given the current state of D, retroactively fitting
> non-nullable references/pointers by default is impossible, unwanted,
> and simply a bloody stupid idea.

Unfortunately, it is that sort of requirement that resulted in C++ being not much better than C, but I have to agree that D2 has to first solidify itself into a production capable state, this is one of the biggest challenges it has right now.

However thinking ahead...

If D as a language is to evolve sensibly, it requires a means to make breaking changes, otherwise it will remain stuck with less than optimal design choices, and in some cases really bad ones. So it seems we do not have a practical means to evolve, other than making mediocre changes once every 8 years or so, as we saw with the C++11 update. Do we really want that to happen with D?

This is a separate topic, but perhaps using this feature can help get around the inability to evolve problem?
http://dlang.org/version.html

--rt

October 05, 2012
On Saturday, 15 September 2012 at 12:38:53 UTC, Henning Pohl wrote:
> The way D is dealing with classes reminds me of pointers because you can null them. C++'s references cannot (of course you can do some nasty casting). So you can be sure to have a valid well-defined object. But then there is always the ownership problem which renders them more dangerous as they seem to be. D resolves this problem using a garbage collector.
>
> So why not combine the advantages of C++ references "always there" guarantee and D's garbage collector and make D's references not nullable? If you want it to be nullable, you can still make use of real pointers. Pointers can be converted to references by implicitly do a runtime check if the pointer is not null and references can be converted back to pointers.
>
> I guess you had good reasons about choosing the nullable version of D references. Explain it to me, please.

Just some links.
Info at least from 2004.

http://blogs.msdn.com/b/ericlippert/archive/2012/07/17/should-c-warn-on-null-dereference.aspx

http://blogs.msdn.com/b/cyrusn/archive/2005/04/25/411617.aspx

http://devtalk.net/csharp/chained-null-checks-and-the-maybe-monad/

google "site:msdn.com non nullable"

Personaly I think that "right tool/pattern for right job/task" is best solution. Maybe problem not in nullable references.

October 06, 2012
On 10/05/2012 08:31 AM, Regan Heath wrote:
> On Fri, 05 Oct 2012 05:19:13 +0100, Alex Burton
> <alexibureplacewithzero@gmail.com> wrote:
>
>> On Saturday, 15 September 2012 at 17:51:39 UTC, Jonathan M Davis wrote:
>>> On Saturday, September 15, 2012 19:35:44 Alex Rønne Petersen wrote:
>>>> Out of curiosity: Why? How often does your code actually accept null as
>>>> a valid state of a class reference?
>>>
>>> I have no idea. I know that it's a non-negligible amount of the time,
>>> though
>>> it's certainly true that they normally have values. But null is how you
>>> indicate that a reference has no value. The same goes for arrays and
>>> pointers.
>>> Sometimes it's useful to have null and sometimes it's useful to know
>>> that a
>>> value can't be null. I confess though that I find it very surprising
>>> how much
>>> some people push for non-nullable references, since I've never really
>>> found
>>> null to be a problem. Sure, once in a while, you get a null
>>> pointer/reference
>>> and something blows up, but that's very rare in my experience, so I
>>> can't help
>>> but think that people who hit issues with null pointers on a regular
>>> basis are
>>> doing something wrong.
>>>
>>> - Jonathan M Davis
>>
>> In my experience this sort of attutide is not workable in projects
>> with more than one developer.
>
> Almost all my work is on projects with multiple developers in C/C++ and
> making extensive use of null.
>
>> It all works OK if everyone knows the 'rules' about when to check for
>> null and when not to.
>
> As every good C/C++ developer does. The rule is simple, always check for
> nulls on input passed to "public" functions/methods. What you do with
> internal protected and private functions and methods is up to you (I use
> assert).
>
>> Telling team members that find bugs caused by your null references
>> that they are doing it wrong and next time should check for null is a
>> poor substitute for having the language define the rules.
>
> Having language defined rules is a nice added /bonus/ it doesn't let you
> off the hook when it comes to being "null safe" in your code.
>
>> A defensive attitude of checking for null everywhere like I have seen
>> in many C++ projects makes the code ugly.
>
> That's a matter of opinion. I like to see null checks at the top of a
> function or method, it makes it far more likely to be safe and it means
> I can ignore the possibility of null from then on - making the code much
> cleaner.
>
> R
>

I find this to be very suboptimal at the least.

This prevents null values from traveling "up" the stack, but still allows them to move "down" (as return values) and allows them to survive multiple unrelated function calls.

It catches null values once they've already ended up in a place they shouldn't be.  Too late.

Nulls can also be placed into variables within structs or classes that then get passed around.  Checking for those can require some complex traversal: impractical for casual one-off checks at the start of a function in some cases.

void main()
{
	void* x = a(b());
	c();
	while(goobledegook)
	{
		x = p();
		d(x);
	}
	e(x); /+ Crash! x is null. +/
}

Where did x's null value come from?  Not a. Not p; the while loop happened to be never executed.  To say "b" would be closer, but still imprecise.  Actually it was created in the q() function that was called by u() that was called by b() which then created a class that held the null value and was passed to a() that then dereferenced the class and returned the value stored in the class that happened to be null.  nulls create very non-local bugs, and that's why they frustrate me to no end sometimes.

What I really want to know is where errant null values come FROM.

I also want to know this /at compile time/, because debugging run-time errors is time consuming and debugging compile-time errors is not.

The above example could yield the unchecked null assignment at compile time if all of the types involved were typed as non-nullable, except for the very bare minimum that needs to be nullable.  If something is explicitly nullable, then its enclosing function/class is responsible for handling null conditions before passing it into non-nullable space. 
 If a function/class with nullable state tries to pass a null value into non-nullable space, then it is a bug.  This contains the non-locality of null values as much as is reasonably possible.

Additionally, it might be nice to have a runtime nullable type that uses its object file's debugging information to remember which file/function/line that its null value originated from (if it happens to be null at all).  This would make for some even better diagnostics when code that HAS to deal with null values eventually breaks and needs to dump a stack trace on some poor unsuspecting sap (that isn't me) or ideally sends an email to the responsible staff (which is me).

October 06, 2012
On 10/03/2012 01:31 PM, "Franciszek Czekała" <home@valentimex.com>" wrote:
> On Wednesday, 3 October 2012 at 16:33:15 UTC, Simen Kjaeraas wrote:
>> On 2012-10-03, 18:12, wrote:
>>
>
>> They make sure you never pass null to a function that doesn't expect
>> null - I'd say that's a nice advantage.
>
> No, it is meaningless. If you have a class which is supposed to hold a
> prime number and you pass it to a function are you going to check each
> time that the value is indeed prime? That would kill the efficiency of
> your program guaranteed. So you would be happy to know that the
> reference is non-null but you would take it for granted the value is
> indeed prime? Does it make any sense?
> I maintain that this non-null "advantage" does not warrant to make the
> language more complicated even by a tiny bit. It is dwarfed by normal
> considerations related to program correctness.
>

It would be cool to have templates like this:

51.  bool isPrime(int val)
52.  {
53.  	...
54.  }

101. Watch!(int,&isPrime) primeNumber = primeGenerator();
102. primeNumber = 8;
103. doStuff();
104. checkPrime(primeNumber); /+ Crash! +/
Error: checkPrime(primeNumber): primeNumber is not prime.
primeNumber: isPrime returned false after assignment at
  (foobar.d, line 102)

~or~

101. Constrain!(int,&isPrime) primeNumber = primeGenerator();
102. primeNumber = 8; /+ Crash! +/
103. doStuff();
104. checkPrime(primeNumber);
foobar.d, line 102: isPrime returned false after assignment.

For convenience one could define this:
alias Constrain!(int,&isPrime) PrimeInt;

I think this would be sufficient for the cases that are considerable less common than errant null references.  I do think these capabilities should exist.  Assumptions suck: allowing invalid data to propogate in non-local ways is BAD.

> With default null references:
> A)either null is an expected non-value for the type (like in the chess
> example), checking for it is part of normal processing then
> B) or null is not a valid value, then there is no need to check for it.
> If you get a null reference it is a bug. It is like getting a 15 for
> your prime number. You do not put checks like that in your code. You
> test your prime generation routine not the consumers. If your function
> gets a null reference when it should not, some other part of your
> program is buggy. You do not process bugs in your code - you remove them
> from it.
>

Please explain how a program printing
Segmentation fault
tells me where the null came from?

If I'm lucky, I even get a stack trace, which is still not good enough in the cases that are actually non-trivial to solve.

My problem with using nullable values /everywhere/ is that they make it very difficult to determine exactly what code is /producing/ the null. If there's no stack trace information then it isn't even possible to know where the consumer is in a lot of cases.

> However with D, dereferencing an uninitialized reference is well defined
> - null is not random data: you get a well-defined exception and you know
> you are dealing with unitialized data. This is easy to fix. You just go
> up the stack and check where the reference comes from. Much easier
> probably than finding out why your prime numbers turn out to be
> divisible by 3. How about introducing some syntax that will rule this out?
>

You... you... you /JUST/ go up the stack.  Nope.

I've been betrayed by this approach many times in the past.  Can you tell? ;)

Copy-pasta from my previous post:

void main()
{
    void* x = a(b());
    c();
    while(goobledegook)
    {
        x = p();
        d(x);
    }
    e(x); /+ Crash! x is null. +/
}

Where did x's null value come from?  Not a. Not p; the while loop happened to be never executed.  To say "b" would be closer, but still imprecise.  Actually it was created in the q() function that was called by u() that was called by b() which then created a class that held the null value and was passed to a() that then dereferenced the class and returned the value stored in the class that happened to be null.  nulls create very non-local bugs, and that's why they frustrate me to no end sometimes.

(end of copy-pasta)

> To quote (loosely) Mr. Walter Bright from another discussion: how many
> current bugs in dmd are related to default null references?

I don't know, but when I was trying to debug a certain dmd bug that was blocking my progress.  It took a day of figuring out what was creating the damn null values.  That could have been much faster.  The bug was not caused by null values, but they severely complicated debugging.  So no, I don't want to do that work.  I want the compiler to do it for me. 
 (I gave up on fixing that bug because it required rewriting non-trivial portions of dmd.)

----------------------

If you have not experienced these frustrations, then you are very fortunate.  Please spread your fortune and be more understanding of the problems faced by others.
2 3 4 5 6 7 8 9 10 11 12 13
Top | Discussion index | About this forum | D home