Who wants non-nullable types (I do, I do!)?
Many people were intrigued at Tech-Ed when Anders revealed the deep language integration we were giving to the new System.Nullable<A> type. I could go more in depth into how it works, but for now I'll just to briefly explain it. Nullable<A> attempts to give a type that everyone can use when trying to represent alue types that have no value. This concept routinely comes up in database programming where you can have a field like 'age' that can be set to 'null' which indicates that you don't know what the value actually is.
Basically we've special cased Nullable<A> to be mixin of the form: Nullable<A> : A. (we also let you simplify how you type it by writing “A?” to mean the same thing). So where you need an A you can use an A? and you can treat an A? (more or less) as an A. For example you could do:
int? a;
int? b;
//stuff
int? c = a + b;
I.e. we've allowed you to use the '+' operator on the int? the same way you'd use it on an int. If eitehr of hte arguments are unset then c will be unset.
There's been a lot of debate over many parts of this feature, but one of the things that has come up is that we seem to be multiplying the number of ways you can say that something is null, and we've also added confusion into how all the different 'null' values interact. What's interesting is that we haven't helped say something that many people want to say, namely that something is not null. Checking that something is not null and dieing because of it is such a common behavior that i ended up writing something to handle it for me:
public static class Argument
{
public static class Validate
{
public static void NotNull(object argument, string name)
{
Debug.Assert(argument != null);
if (argument == null)
{
throw new ArgumentNullException(name);
}
}
//other validation methods
}
//other things you can do on an argument
}
You would then use this by writing:
Argument.Validate.NotNull(foo, "foo");
I'd much rather have a built in langauge feature, compile time checking, and BCL support for that kind of construct. i.e. the ability to say something like:
string! s
Where the ! means “can't be null”. You could then decorate your methods ending up with things like: public string! SubString(int start, int end);, etc.
You could automatically pass an A! anywhere where you needed an A, and using the ! operator you could convert an A into an A! (throwing if the object as null).
This, like const, would require work but would be so valuable for the compile time verification of what is such a common problem. Threading this through the BCL would make it safer, potentially catch bugs and would benefit everyone (except the poor BCL authors who would haveto retrofit their code). But really, would the cost be that great? You'd be able to _get rid of_ null reference checks everywhere. If the compiler were really nice it would look for these issue and give you warnings. i.e.:
if (null == foo) //warns: foo can never be null, express will always be false
return foo //warns: return type of the method is a string but you always return a string! (a non null string, not a statement of exclamation ;-) ) consider having your method return type be string! instead.
What do you think? Useful?
---
Edit: Eric discusses Nullable<A> as well
Edit: Luke discusses it as well, and delves into the issues integrating with the current BLC.
Comments
- Anonymous
June 03, 2004
A programmer after my own heart.
My orininal thought would be to just allow for an implicit conversion of a nullable type to bool for purposes of if checking... see here
http://schweitn.blogspot.com/2004/04/my-syntactic-sweet-tooth.html
But now that I've read this... I think that non-nullable types would rock. It would almost be like going back to the days of user defined class instances being allocated on the stack... sometimes I miss the C++ days. - Anonymous
June 03, 2004
The comment has been removed - Anonymous
June 03, 2004
im with you - nullable should go hand in hand with non-nullable.
in fact, non-nullable types have behavior that is a strict subset of normal variables, and so introducing them should be a problem-free excersise.
nullable types are a superset of the behaviour of normal types, and changing a normal variable to a nullable variable can potentially cause problems. - Anonymous
June 03, 2004
I think the usefullness of something like this is questionable. For example, what happens when you set a string! to the return value of a function that returns string and that value is null sometimes. There is no compile time check for that, so you still have to check that value in your code (unless you want ArgumentNullExceptions) everywhere and the usefullness is lost. So from re-usability standpoint this idea works great. I grab an API and it declares object! to be one of the parameters so I know I cannot pass a null value. This means that anything that I get from an outside method would have to be checked by me before I could pass it into this API method. But then that doesn't really help the consumer of the API, does it? - Anonymous
June 03, 2004
I like it :)
Something that I would like to have in C# is const, but I could live with A!
Another thing that I would like to see in the framework is an INullable interface, just the one in the System.Datra.SqlTypes byt I would like to have it inte ht System namespace. There is a INullable today, but it's internal and used by Nullable<T>. The reason why I want to have a INullable interface with an IsNull property, is that I could then create nullable object, for example NullCustomer. Martin Fowler wrote about Null objects in his refactoring book, and I have used it in my solutions and it's very useful. - Anonymous
June 03, 2004
As someone who works with databases and financial software - where 0 and null are two different beasts entirely - I cannot wait for my pain to end.
I'm currently using types I created because the SqlTypes weren't serializable. (And Cyrus, if you have any pull at all, would you ask SOMEONE there why all the base types are sealed? Please? Argh..)
Here's waiting anxiously. - Anonymous
June 03, 2004
Nick: The C++ days... shudder :-) - Anonymous
June 03, 2004
Giampero: you would not be allowed to set a string! to a function that returns a string. You would have to explictly check for null in that case (by using something like the ! operator i talked about above). It could look something like this:
string! str = !! methodThatReturnsAPotentiallyNullValuedString()
That would become a runtime check. If you got an isntance back it would be ok, if you got a null value back it would throw. Similar to how casting can throw today at runtime.
The benefit is within your own code, and if the APIs are used you will, in general have less checking to do than today. (Note: today you'd currently have to check that that return type was not null as well). - Anonymous
June 03, 2004
- What will be an Exception error message for this test case ?
A a = null;
A! na = !A; // <-- ??
Will it be meanless System.NullPointerException ??
How do you expect user to be able decrypt this message and fix origin if his boss asking him to complete report in 10 minutes ;o) ??
I expect more user-friendly exceptions given to user if you are unable to avoid them.
2. Also I do not see big benefits for this.
It's about that same that Java users have to do with (MyObject) Collection.get(i) without actually knowledge of data inside collection.
The only benefit is early null error detection if value is not used immediately. But I always check values before storing them anythere. This ! operator will be simply a shortcut for this.
MSFT can possibly implement it without changing CLR at compiler level as Microsoft Extension ;o)
- Anonymous
June 03, 2004
Giampero, as the consumer of that API you have to do that check anyways, because if you don't the API will throw an NullReferenceException (or ArgumentNullException) when you pass in the null value. - Anonymous
June 03, 2004
Brian: I'll look into it and see if there's someone who can answer your question for you. - Anonymous
June 03, 2004
The comment has been removed - Anonymous
June 03, 2004
The comment has been removed - Anonymous
June 03, 2004
Hi, Cyrus: Luke Hutteman (of SharpReader fame) proposed this very idea yesterday: http://www.hutteman.com/weblog/2004/06/02-181.html
+1 from me! - Anonymous
June 03, 2004
The comment has been removed - Anonymous
June 03, 2004
Ok, I got it now. Took me a while (and a few rebuttle examples that didn't pan out)to realize where the benefit is.
But one more thing is, what happens to the BCL APIs that don't use this construct (which there are a lot of)? If you don't change the APIs, what is the point in having this? If you do change the APIs you break backwards compatibility because everyone would need to use that new operator.
Basically it just seems to me that is making things easier on the API writer's end and harder on the consumers end (more casts). A language convenience like this could be nice but really doesn't let you do something that you couldn't do before (unlike Nullable<T>). <Opinion>Seems like more work than the gain.</Opinion> - Anonymous
June 03, 2004
Synchronisity... I had this exact discussion today about the usefulness of non-nullable types that complement nullable types. I want it!
There are issues, of course... A non-null type could not be a member of a struct, since it must have a constructor that sets all members to their default values (null for ref types). A non-null class member would have to be initialized in the constructor of the class, but how would one guarantee that the member isn't accessed (indirectly, via some other method called from the constructor) before the class has finished constructing? So, it seems that you wouldn't be able to guarantee with 100% certainty, even if your program is verifiably type safe, that an instance of a non-null type is never null, but I still think this is a useful feature. - Anonymous
June 03, 2004
I do too, I do too!
Non-nulls force types to define their behaviour when their value is unknown, instead of letting every one assume what the behaviour is.
A good example is numbers. Instead checking for null:
if (number != null && number < maximum) ...
The behaviour for a "null number" is defined in for instance NaN. It will always return false for tests, so the example becomes:
if (number < maximum) ... - Anonymous
June 03, 2004
One confusion I'm seeing is that cardinality is being confused with reference versus value (i.e. heap allocated versus stack allocated). They aren't actually the same thing, but by default a cardinality of 0 or 1 is associated with reference variables and a cardinality of exactly 1 is associated with value types. They don't have to be and in my opinion they shouldn't be.
IMO, by default all objects should have cardinality 0 or 1 (!). So string should be the same as string! and int should be the same as int!.
As a programmer, I shouldn't really care how variables are stored (i.e. stack versus heap). That's really the compiler's problem. But I always care about the structure of my data (i.e. its types and those variable's cardinalities).
Orion Adrian - Anonymous
June 03, 2004
The comment has been removed - Anonymous
June 03, 2004
Orion: Could you explain a little bit more about what it means for an object ot have cardinality? I think i understand, but i'd appreciate more information.
Also, why should things, by default, be allowed to be valueless. It seems safer and more clear to make them valued by default and to only explicitly make them valueless if the need arises. - Anonymous
June 03, 2004
The comment has been removed - Anonymous
June 03, 2004
AT: Excellent points. BTW this has nothing to do with performance. Also, see in my message how I implemented that pattern.
Note though. With the current model both the API consumer and producer must agree on this contract and ensure that it is maintained. Of course, this is no different from any other API contract, however, in this case it is such a common occurrance (literally hundreds of calls and methods i must document about this single issue) that the saving to both consumer and producer woudl be great (IMO).
Also, why would you limit Types in that way? People wanted nullable structs because they foudn structs too limiting not being able to be null. Why not allow symmetry in teh type system? Nullable/Non-nullable versions of every type? - Anonymous
June 03, 2004
AT: I like your idea of a single unified location to verify arguments. That's what my 'Argument' class is intended to provide.
I see this more as a completeness argument (and maybe i should have pitched it that way) with the added benefit that you get strong compile time safety against a common class of problems. - Anonymous
June 03, 2004
The comment has been removed - Anonymous
June 03, 2004
The comment has been removed - Anonymous
June 03, 2004
The comment has been removed - Anonymous
June 03, 2004
AT: Another point, while I wrote the validation code, I don't like it. There's already duplication in it that I want to refactor. Namely the passing of the argument and the name. It's small, but it is a smell and it's more book keeping for me to keep track of. The internal call also won't update my XML docs, nor will it make my callers verify that their values are not null before passing them in. All in all we've pushed the issue to failing at runtime which always has the chance of getting missed. :( - Anonymous
June 03, 2004
Simply a list of related links:
http://research.microsoft.com/~maf/Papers/non-null.pdf
http://cdsmith.twu.net/professional/java/pontifications/nonnull.html
Or more generic problem:
http://archive.eiffel.com/doc/manuals/technology/contract/ - Anonymous
June 03, 2004
The comment has been removed - Anonymous
June 03, 2004
My only problem with nulls comes down to displaying values (on a form or on the web). How many times do I have to write (sorry for the VB code).
If not isnull(databaseField) then
textbox1.text=format(databaseField,"C")
Else
textbox1.text="$0.00"
End If
What I want to write is
textbox1.text=format(ifnull(databaseField,"0"),"C")
Basicly if databaseField is null return "0" otherwise return databaseField
FIX THIS PLEASE!!! - Anonymous
June 03, 2004
Steve: There is built-in function in VB for Access - Nz(checkValue,valueIfNull). You can create example of own or use built-in if such exists. - Anonymous
June 03, 2004
"My only problem with nulls comes down to displaying values (on a form or on the web). How many times do I have to write (sorry for the VB code).
If not isnull(databaseField) then
textbox1.text=format(databaseField,"C")
Else
textbox1.text="$0.00"
End If
What I want to write is
textbox1.text=format(ifnull(databaseField,"0"),"C") "
The problem I see with this is why the field is null in the first place. This seems more a problem to do with the constraints (or lack of) placed on the data and less a problem to do with displaying it. $0.00 and null are not the same. This isn't something I'd like to see even if it was simple to implement.
Ask this question, "Why is databaseField null and not 0, and if it's null why would I want to display it as 0?"
Orion Adrian - Anonymous
June 03, 2004
Steve: C# also has support for this through the Null-Coalescing operator ??. It works in the following manner:
expr = expression1 ?? expression2
if expression1 evaluates to a non-null value then it is the value of 'expr'. If expression1 evaluates to a null value the expression2 is the value of 'expr'. So in C# you could write:
textbox1.text = format(databaseField, "C") ?? "$0.00"; - Anonymous
June 03, 2004
Orion: Because there's a difference between your view and the underlying representation behing the scenes. It would be just as reasonable to have:
textbox1.text = format(databaseField, "C") ?? "Squashed Cockroach";
However, it's important for the underlying data to have the ability to say "this does not have a cost" as opposed to "this cost is nothing". - Anonymous
June 03, 2004
"textbox1.text = format(databaseField, "C") ?? "Squashed Cockroach";
However, it's important for the underlying data to have the ability to say "this does not have a cost" as opposed to "this cost is nothing"."
This I agree with. I guess I should have been more clear. I just wanted to express that in the specific example the problem wasn't in the formatting, it was in the data structure. However how to display null is always a problem. That and whether to display it at all.
Orion Adrian - Anonymous
June 03, 2004
Orion: Fascinating information about the cardinality of things. I've never thought of variables as sets before, but having that consistancy would be great across the entire language.
Also, thanks much for the articles. I'll see what i can do about spreading that information around in here so we can consider it for future langauge improvements.
Knowing how much people care about these things goes a long way in deciding what we'll be doing in the future. - Anonymous
June 03, 2004
My point was not to say there is no difference between $0.00 and NULL. My point was that there is not easy way (in VB.net, guess there is in C#) to convert this null value into something that I can display to the user (ie. N/A or $0.00 or "VALUE NOT SET" or what ever is required.)
By the way I wrote my own function which emulates foxpro NVL to do this but it requires 5 overloaded functions (boolean, date, integer, string, decimal) I would rather have it built in. - Anonymous
June 03, 2004
The comment has been removed - Anonymous
June 03, 2004
Ah, ok -- Cyrus has beaten me with the clue stick offline, so I now really understand how Nulllable in C# is going to work. So ignore the previous post :-)
(That said, it seems to me that overloading the "null" literal to mean both "unknown" and "all-zero-pointer" is asking for confusion...) - Anonymous
June 03, 2004
Neil: I've got my own reservations about this issue :-)
That said, i think the readers might appreciate an example where this could be quite confusing given our current understanding of how null works in C# now. Would you like to show that? - Anonymous
June 03, 2004
Please please please implement A!. Scratch nullable types, A! is 10 times more useful. - Anonymous
June 04, 2004
The comment has been removed - Anonymous
June 04, 2004
The comment has been removed - Anonymous
June 04, 2004
Isaac, Nicole - Microsoft already implemented FxCop-like validator at IL level fully compatible with current CLR runtime using attributes.
See http://research.microsoft.com/~maf/Papers/non-null.pdf
I hope they will be able to put it in production from research fast.
Note - in addition to simply null-checking they was able to reveal a lot of others problems. - Anonymous
June 04, 2004
Thanks AT, I've seen that paper. It's a pity that nullable is taken to be the norm, maybe that's inevitable when the implementation relies on attributes.
It's almost worth installing JRE just to try out Nice ;-) - Anonymous
June 07, 2004
AT: The Fahndrich and Leino paper describes the partial design and implementation of a static checker. This is potentially very, very far from putting into production a complete non-nullable type system in as tightly integrated a manner as originally described by Cyrus.
To be honest, I would see quite a bit of the work described in the paper as an almost equally good starting point for a general declarative validation framework. Many (most?) of the design challenges addressed would be very similar if one were to simply substitute the more general notion of "is valid" for its "is not null" subset.
At any rate, I'm very glad that folks at Microsoft are working on this sort of thing, but it doesn't change my opinion in any way regarding my preference for implementation of a declarative validation framework before non-nullable types. - Anonymous
June 07, 2004
This is infact is/was part of x#/Xen/C-omega. It is actually very useful construct. It allows the code using that particular variable assurance that it will never be null.
We did indeed look at adding this to C# in Whidbey along side of the new nullable types. However, we realized that most code written to date in the C# language, API's, etc, already encode non-null semantics using just the normal reference types and runtime checks. Adding the new non-null type would lead to people choosing to either continue to use plain reference types as parameters, etc, or using the new non-null types to encode non-nullness. The resulting codebases/frameworks would eventually grow unwieldy because both systems would be in use at the same time.
We chose to forgo it for the time being. It would have been better to introduce this right from the get go.
Matt - Anonymous
June 09, 2004
<q>We chose to forgo it for the time being. It would have been better to introduce this right from the get go. </q>
<p>Isn't there a way to get around this now. Even if it's just part of the documentation only. It would at least allow tools to analyze the code more easily in the future and the signature itself would give you very valuable information without having to do a lookup (which I do all the time because this information isn't readily available at the "intellisense level".)
Orion Adrian - Anonymous
April 24, 2005
For those of you who don't read the comments made on other posts of
mine, you might be unaware about...