View mode: basic / threaded / horizontal-split · Log in · Help
October 05, 2012
Re: References in D
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
Re: References in D
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
Re: References in D
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
Re: References in D
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
Re: References in D
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
Re: References in D
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
Re: References in D
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
Re: References in D
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
Re: References in D
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
Re: References in D
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.
8 9 10 11 12 13
Top | Discussion index | About this forum | D home