Hi all, I'll answer a few things that came up in the discussion:
Why do I think that enforcing nullchecking by assignment 'non-null = (!)nullable' is good, and enforcing checking when accessing members is bad? Well: the first one is a contract programming technique, the second is not.I disagree. I think one of the most basic contracts is that an object is non-null when you access its members. Its an implicit contract that is very important.
But there's a difference between having obtained a reference to some external object (that means transfer of data, and that means a need to validate obtained data, and that means having a nullcheck), and having a reference to object that is being used in the current scope. The latter situation means that we're managing object's internal state (let's count object existence or being null as it's internal state), so if this state is wrong, it does not mean "whoops, I've accidentally a whole object! let's give some checking here then!", it means rather that the algorithm has bugs. And I don't think that contracts should be used to maintain algorithm flow, they're for data validation only. What is more, calling methods on objects is not something that I'd call data transfer. On a low, technical level it's indeed passing a pointer to other function, but when we're considering it on the level of object-oriented programming, it's just using an object. Just like using a number when we perform floating-point division. Whether the used object is valid or not should be not checked, or checked at run-time; the example can be division by zero. It's quite uncommon to check if divisor != 0, but if it is zero at run time, SIGFPE will be raised (and most probably - ignored). Therefore I suggest treating objects the same way - when there's no object transfer between different scopes / modules, don't contract.
I think the idea behind a language with non-null types is to use algorithms that never need a null value except to handle inputs and outputs. IMO, if you have a nullable variable, you have a design problem.
And:
If what you mean would look like this: Foo? a = bar(); // a is unsafe and can't be accessed if (null != a) { // a is safe to use in this block } then I don't really see how that is any different than what we have now: Foo? a = bar(); // a is unsafe and can't be accessed if (null != a) { Foo b = (!)a; // b is now safe to use }
It's not true. If I'm using lots of functions, that return nullables, doing 'if(foo != null) non_null_bar = (!) foo;' will be disrupting: - it's too verbose, obfuscates code and makes programmer considering porting his code to javascript - it's increases memory and decreases performance, because now there are two references per object, and they probably won't be optimized out by gcc (because we call unref() on both of them when leaving function). The question if returning null to indicate not completing operation is a "right way", or the exceptions would be better indicator of such situation, is a good topic to have a little flame war, but as far as such flame wars occur sometimes and take casualties, I think that vala should enable both these techniques. And as for now, using experimental non-null makes "return null on failure" extremely unfriendly technique.
For the rest of them, you'll just have one extra assert(), which is not a problem but rather a good practice.Isn't the point of having non-nullable types to eliminate runtime checks? When testing your program, its very easy to miss cases that would lead to the assertion failing, which is still a bug.
It's true, and it's also true that experimental non-null at this moment is a perfect compile-time checking utility (as far as frustrated users won't start putting (!) everywhere to silence "these stupid errors"). And a very unfriendly utility too. In my opinion, current usage of (!) operator is contrary to all purpose of experimental non-null feature. Currently we use this operator to indicate "this assignment is safe!", and IMO this should be "this assignment is dangerous, but I take it anyway". The typical current use case: SomeType? foo = bar(); if(foo == null) return; //dear compiler, I want to check if foo is not null... baz((!)foo); //sweetheart, we both know that the exclamation mark means that passing foo to baz() is perfectly safe! foo2 = (!)foo;//and here too, because you my darling are sweet blondie who never understands what I'm talking about if I don't repeat it. But sometimes it looks like this: SomeType foo = (!) bar(baz); /* Dear compiler, I suppose that bar() will never return null here, because I know that baz is here constrained is such way, that it will never make bar() produce null. Yes my darling, I know what I'm doing, as I wrote above, the exclamation mark means that this assignment is perfectly safe! Believe me! I'm not putting nullcheck here, because bar() is a one-liner, probably will be inlined, and I'm using lots of similar functions in my whole program. Checking everytime would make it run much slower */ And all the idea behind compile-time checking is now worthless. In the ideal world it would look like this: SomeType? foo = bar(); if(foo == null) return; //dear compiler, I want to check if foo is not null... baz(foo); //now my smart little beast we both know that this place wouldn't be reached if foo were null, so passing foo here is safe, and therefore I'm not putting (!), and there will be no error nor warning. And: SomeType foo = (!) bar(baz); /* Dear compiler, I know that static code analysis would reveal, that bar will never return null here. But you seem to not know about it. Therefore I put here an exclamation mark, and exclamation mark always brings to my mind screaming, fear and danger. Therefore please accept this exclamation mark as a proof that I'm aware of writing dangerous code and of course I know that you'll issue compiler warning in every place, where i put these operators. (!)*/ Well. That's all for today. BTW, don't you know why I did not receive mail with my original post yesterday? I have set receiving my own mail too, but it did not reach me... best regards, AW. -- Mój klucz publiczny o identyfikatorze 1024D/E12C5A4C znajduje się na serwerze hkp://keys.gnupg.net My public key with signature 1024D/E12C5A4C is on the server hkp://keys.gnupg.net
Attachment:
signature.asc
Description: PGP signature