On 10/06/2012 04:18 AM, "Franciszek Czekała" <h...@valentimex.com>" wrote:
On Saturday, 6 October 2012 at 04:10:28 UTC, Chad J wrote:

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

returned the value stored in the class that happened to be null.

Happened? "I was driving carefully and then it happened I drove into the
tree, officer." Every function should define its interface, its contract
with the outside world. If a() function returns a pointer it is a part
of the contract whether it can be null. Two possibilities:

A) The contract says it can be null. Then it is your duty to check for
null. Period. Learn to read the signs before you start driving. You
assinged the value without checking, it is your fault, not a()'s, not
the language's.


I am unconvinced by the driving analogy. When driving, most of the important bits become muscle memory (acceleration, braking, turn signals, etc) and the rest falls under the category of "be aware". The factor in our advantage is that awareness in driving usually only requires you to focus on one thing at a time: "turn your head before changing lanes or turning", "look at the sides of the road", "check your rear view", etc.

Programming involves the management of complex logical relationships. It is more akin to mathematics. I could continue, but I'll stop here and leave it at "I'm unconvinced".

Even if I grant the premise, I'll expand on what Timon wrote:
We'd have a lot less accidents if well-designed robots drove our vehicles for us (with manual overrides, of course).

B) The description of a() says the return value cannot be null. Then a()
should check its return value before returning or make otherwise sure it
is not null. If it returns null it is a bug. One of the infinite number
of possible bugs that can happen. Again it is not the problem of the
language. The problem of divergence of specification and code is a human
problem that cannot be solved formally. Insistance on formal tools is a
misunderstanding that leads to design bloat and eventually failure (Ada).


As I understand it, you would have written my code snippet this way:

void main()
{
    MyType j = b();
    assert( j !is null );
    assert( j.qux !is null );
    assert( j.qux.yarly !is null ); /+ Crash! yarly is null. +/

    void* x = a(j);
    assert( x !is null );

    c();
    while(goobledegook)
    {
        x = p();
        assert(x !is null);

        d(x);

        // Note: be sure to put this one in!
        //   The previous dev forgot it...
        assert(x !is null);
    }
    e(x);
}

If you feel that this is misrepresentative, then please provide your own. As it stands: give me non-null types, because I like mine better.

D competes directly with C++ as Ada did before. Ada drowned under the
weight of its "safety" and so will D if it goes the same route. The only
thing needed now are mature compilers and good systems API integration.
If anything I would rather consider removing features from the language
than adding them.



Given the two snippets I currently see in my mind's eye that represent our two different philosophies, I do not see the "weight" of this safety. I think it makes for much more concise code, and a faster development process because I don't have to worry about inane stuff like checking for nulls. If I don't have to worry about inane stuff, then I can allocate those short-term memory slots to solving problems that are actually novel and interesting.

Reply via email to