On 10/03/2012 01:31 PM, "Franciszek Czekała" <h...@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.