Nullable types in C# (part 2)
I was reading the C# 2.0 language spec and trying to wrap my brain around the new support for System.Nullable<A> in the C# language. There are just a couple of parts that I keep running into and not quite getting. The first is the following set of statements:
1.
A nullable type can represent all values of its underlying type plus an additional null value.
2.
The underlying type of a nullable type may itself be a nullable type. Thus, types such as int?? and int??? are permitted, although they can represent no more values than int?.
I just don't know how to reconcile these statements. The specification for Nullable is as follows:
public struct Nullable<A> where A : struct {
bool HasValue { get; }
A Value { get; }
public Nullable(A value);
}
The first statement says that a nullable type can represent all values of its underlying type plus an additional null value. That plus is essential. It means that given any value type you can always get one extra value to indicate something out of the normal set of values the type can be set to. For example, if your type 'A' is 'int' then you have 1 extra value that you can use outside of the regular -2^31 <-> 2^31 -1 regular values. This expressiveness is necessary because there are many cases where you just don't have a value that you can spare for 'special' meaning.
So that's all well and good. However, i then tried to merge that with the next sentence: "The underlying type of a nullable type may itself be a nullable type. Thus, types such as int?? and int??? are permitted, although they can represent no more values than int?." This doesn't seem to make much sense ot me. Nullable<A> is a value type, it has a certain range of allowable values. The first sentence implied that Nullable<Nullable<A>> would allow us to represent all values of the underlying type (Nullable<A>) plus an additional null value. However, the second claim says that Nullable<Nullable<A>> is no more expressive than Nullable<A>... I'm not sure how to reconcile these ideas especially in the light of another sentence which claims: "C#’s nullable types solve this long standing problem by providing complete and integrated support for nullable forms of all value types."
Nullable is a value type. However, it doesn't seem to be completely supported and integrated since it does not seem possible to encode an extra null value into the type through the use of a Nullable-Nullable.
Is this really a problem? Possibly, possibly not. Will this actuall affect anybody, or am I getting myself into a twist over nothing? I was thinking of places that one would use Nullable's and I discovered a scenario where this could be a problem. Consider the following code:
delegate bool Predicate<A>(A a);
IList<A> {
...
A? Find(Predicate<A> p);
...
}
Find will search the list and return the first item that matches the delegate, or a nullable-A with no value if no element is found.
For example, you could use that in the following manner:
IList<int> ages = GetAges();
...
int? adult = ages.Find(delegate(int i) { return i >= 21; });
Which looks for an element in the list greater or equal to 21. That's kinda of useful, and I can see all sorts of ways that that makes many APIs much cleaner. In fact, the spec says it the best with "Another approach is to maintain boolean null indicators in separate fields or variables, but this doesn’t work well for parameters and return values". So, in essence, nullable types allow you to incode that extra bit of boolean state along with the actual value consolidating them into a nice object. (In fact, it's very similar to the IOptional type that Jay, kevin and I used when writing the lazy loader). However, consider the following more complicated case:
IList<int?> ages = GetAgesFromDatabase();
....
int?? unsetValue = ages.Find(delegate(int? i) { return ! i.HasValue; });
Here we're asking the question: "Does the list contain an item that has no value." I.e. does the database column of ages have a row whose value is null for that column. Now lets think about the answer to that question in the following two cases:
1.
The database contains no unset values for the age field. The Find operation will return an int?? that has no value.
2.
The database contains an unset value for the age field. The Find operation will want to return that, but as int?? is not more expressive than int? (and the int? will have no value, since that is what we searched for) it will return an int?? that has no value either
What we seem to have ended up with is an inability to form a very simple sentence. I.e. "is there a nullable that has no value." or "give me the nullable that has no value". This seems like something that one would like to express (especially when dealing with DBs), and we have explicitly removed that ability from you. It's also not clear what this means when we're dealing with different languages. Will all languages have this semantic for Nullable<A>? Or will it just be C#? What expressive power do we gain by adding this restriction? Why not have a simplified system that is totally consistant over all types, or at least over all value types? Thoughts on this would be appreciated. Do any of you have experience with other languages/libraries that have attempted to incorporate this and how they did it/if they were successful at it.
Comments
Anonymous
June 09, 2004
Sorry, I don't have Whidbey yet, so I can only talk in theoretical terms rather than what may actually be implemented, but if you have int?? myValue, then you should be able to refer to myValue.Value.Value (or myValue for short!), myValue.HasValue and myValue.Value.HasValue, giving you two separate nulls. I hope that the problem here is just one of semantics, with statement 2 being slightly badly phrased. Certainly it seems to be saying that int? and int?? are in some way different (so we're not just ignoring the extra '?'), and there is no good reason or way that I can think of to combine both nulls into one.
Whatever, I too would be interested to know how much support for nullable types there will be outside C# if anybody knows.Anonymous
June 09, 2004
The comment has been removedAnonymous
June 09, 2004
Cyrus,
I think you are right when you bring up the concern over the language concerning the set of values represented by nullable types, as well as the exception relating to when a nullable type is itself passed to the Nullable structure. In this case, the language spec should probably be amended to say "all value types (with the exception of the Nullable type)."
Now, onto your scenario with the Predicate. In my opinion, your design is flawed for a number of reasons. The intent of the Find method is to find a member that exists in the list. The members are of a certain type, T. However, your return type is T?, which is odd, as that is not the type represented in the list. This would cause confusion in a number of cases I believe (because now the developer has to remember a specific use case for your Find method, as opposed to sticking to his gut).
Also, you now resort to a null value to represent the on/off state that should also be returned by the Find method. This value is better represented by the boolean type, which you seem to be ignoring (unless you consider the HasValue property, but then that confuses the issue even more, because you are now saying that the meaning of HasValue is the same as "found").
It looks to me that you are trying to cram two values into the return value. You want to return the value itself, as well as whether or not the value is found. I believe that you should use the return value to represent either the value indicating whether or not the value is found, or the value itself (being a wrong value when it is not found), and have a ref or out parameter convey the other value.Anonymous
June 09, 2004
The comment has been removedAnonymous
June 10, 2004
This is a problem in C#'s implementation of nullable types. Because it's a tack-on and not a core part of the language, you're going to end up with problems like this.
Think. Int32 and Double don't contain any special kind of data that makes it any different than the other non-value types in the system.
In C# because nullable is handled with a class int?? and int? are different. Mathematically they shouldn't be and int?? is nonsensical. int?? should always reduce down to int? wherever it pops up. Unfortunately that's probably pretty hard to do with C# the way it is now.
Remember nullability is a property of the type, not a part of the type. Saying something is nullable twice should give the same effect as saying it once.
Remember in set logic null is just the empty set ({ }). An int with a value in it (say 12) would be {12}. An array of 2 ints (say 12 and 13) would be {12, 13} expressed in set notation.
Type constraints are just limit what can go into a set. And all sets should be able to be empty. Or not if you want to limit a variable to only non-empty states.
This is not a problem with the math behind this system, but rather a problem with how C# implemented nullable values types.
Given a variable there are about a half-dozen properties that can be applied to that variable that modify the set it represents.
Minimum and Maxiumum Cardinality (Includes nullability)
Does Order Matter? Should it be Sorted?
Do Multiples Matter?
Type(s) Limited To?
Given these 6 properties, you can represent all variables and non-associative data structures. Unless I'm missing something.
The question becomes, given the simplicity of these, why doesn't the type system include them. Honestly they could be integrated into the type system, removing completely the need for the basic collections.
So how you declare a variable (notation in BNF) is:
('unordered' | 'ordered' | 'asc' | 'desc' )? 'unique'? type ('[' (number | '0') ',' (number | '') ']')? name ';'
If you needed a sorted array (ascending) of ints (minimum 1, maximum many) you could just say:
asc int[1,] myArray;
Say I needed a list of strings (2 to 6) and each value has to be unique.
unique string[2,6] myStrings;
And of course the last type I haven't mentioned is associative arrays which could also be done using this system. Honestly isn't it time we left the hard stuff for the compiler?
Orion AdrianAnonymous
June 10, 2004
Orion: It would be nice, but I am not convinced it fits the C# paradigm well enough. Breaking the paradigm breaks use, no matter how valuable the additions are. It would make for an interesting project, usint Rotor or the mono compiler however.
Cyrus: Nullable is basically immutable. Because of this if the value is a nullable type, it can be null. Now, if that value is null, then the whole structure is null because HasValue tells you if it has a value or if its null. No matter how deep you go null is null is null, there are no levels of granularity. The value is always eventually null and therefore the entire structure is always null
Consider int??? n = null;
The basic resulting operation is
n.HasValue = n.Value.HasValue = n.Value.Value.HasValue = null;Anonymous
June 10, 2004
Err, I forgot to finish that,
[quote]
n.HasValue = n.Value.HasValue = n.Value.Value.HasValue = null;
[/quote]
because
n == n.Value == n.Value.Value
if n.Value.Value == null
then
n.Value == null
and then
n == nullAnonymous
June 10, 2004
Nicholas: You say "It looks to me that you are trying to cram two values into the return value. You want to return the value itself, as well as whether or not the value is found. I believe that you should use the return value to represent either the value indicating whether or not the value is found, or the value itself (being a wrong value when it is not found), and have a ref or out parameter convey the other value."
Yes. That is exactly what I am trying to do. However, is that strange? The spec says:
"Many approaches exist for handling nulls and value types without direct language support, but all have shortcomings. " ... "Another approach is to maintain boolean null indicators in separate fields or variables, but this doesn’t work well for parameters and return values." ... "A nullable type is a structure that combines a value of the underlying type with a boolean null indicator."
To me it seems very clear that the spec is advocating using a nullable value when you need to return the value, but you might not have the value. :-)Anonymous
June 10, 2004
Daniel: Those aren't the semantics:
because
n != n.Value != n.Value.Value
I think I agree with Orion here. ?? is nonsensical. We needed a 'special' Nullable type, or nullable facility on value types so that they could be empty. Having Nullable itself be a value type that can not have a null value is just bizarre.Anonymous
June 10, 2004
real quick correction,
[quote]
n.HasValue = n.Value.HasValue = n.Value.Value.HasValue = null;
[/quote]
should have been either
n.HasValue = n.Value.HasValue = n.Value.Value.HasValue = false;
or
n = n.Value = n.Value.Value = null;
Anyway, n == n.Value == n.Value.Value when you are considering int??? x = null or Nullable<Nullable<Nullable<int>>> x = null because null == null == null. I know you want to consider Nullable as a type with a boolean field, but that can't be because nullable CAN be conceptually null, unlike all other valuetypes. You are basically saying its wrong to make Nullable unable to be nullable because Nullable can already be null, which is about as non-sensical as a thing as I could dream up(something that is already null can be specified as not-null by a higher property, even though it IS null, no matter what you want to believe). Try expanding that to reference semantics and see what kind of mess you find yourself in.
When Nullable::HasValue is false, that is a null, not a value that could hold a null, and should be treated as a null in ALL cases, including being wrapped in another Nullable structure. You can't treat Nullable as null when HasValue is false in some situations and not in others. That would be a mistake of feature killing caliber.
I really don't understand why you want this? It makes no sense and offers no value.Anonymous
June 10, 2004
Oh, btw, I didn't notice you asking in the original post, however
Nullable::Value throws an exception if Nullable::HasValue is false.
If Nullable is initalized with another Nullable, that nullables HasValue is hoisted and so this will affect all languages because it is irrelevent to the language, its how the struct itself works.Anonymous
June 10, 2004
The language really should prevent the idea of using Nullable as a template parameter for itself. That or have special semantics that reduce Nullable<Nullable<T>> to Nullable<T>.
Orion AdrianAnonymous
June 10, 2004
While I don't think that Nullable<NUllable<T>> will automatically become Nullable<T>, I do think that the semantics of Nullable(implicit operators and hoisting) should result in C# semantics that effectivly cause reductionAnonymous
June 10, 2004
Daniel O'Connell
int??? param = null;
This result in simply param.HasValue = false
but param.Value, paramValue.HasValue, param.Value.Value and param.Value.HasValue undefined.
As for sets here is my opinion,
Simply value 1 is different from {1} and {}.
And {1} is different from {{1}}, {{}} and {} as well
{{1}} is different from {{1}}}, {{{}}}, {{}}, and {}.
But {1} and {} are from same class, as well {{1}} {{}} and {}, etc..
I know Von Neumann theory of natural numbers build based on empty set and it's power sets instead of Peano postulates ;o)
http://planetmath.org/encyclopedia/VonNeumannInteger.htmlAnonymous
June 10, 2004
daniel: Nullable's 'HasValue' is only hoisted if it is false, not if it were true. There is no way to get:
int?? to be the same type as int?
That would be saying that:
Nullable<Nullable<T>> is the same as Nullable<T>
which would be akin to saying:
List<List<T>> is a List<T>.
...
Nullable<Nullable<T>> has a property on it called HasValue that returns not a T but a Nullable<T>. Whereas Nullable<T>'s 'HasValue' returns a T. They are very different objects.Anonymous
June 11, 2004
Cyrus:
First, HasValue returns a boolean.
Second, The property Value returns a Nullable<T>, however, that Nullable<T> supports implicit conversion and hoisted operators, therefore can be considered to be T in all ways(except null and type comparison), if my reading of the spec is correct, at any rate.
While they are different objects, they are value objects with the same effective value. And I'm saying that Nullable<T> == Nullable<Nullable<T>> when
they both equal null(which is true for all forms of List<> as well, null == null, even though the language won't compare them due to type safety). The big mistake is assuming that Nullable<T> = null means that Nullable<Nullable<T>> doesn't have to equal null, even though its value IS null.
Consider this, you pass null to the contructor of a container, what does that that container do(assuming null is avalid state)?
1) sets the value to null and moves on?
2) sets the value to not null and pretends it isn't?
3) sets the value to null and pretends it isn't?
You solution for Nullable is #3...that doesn't make sense to me, does it to you really? If a value is null, isn't it already null? Why does the container have to be able to pretend its not even though it really is?
And you have yet to tell me why there is any value in a doubly nullable Nullable<T>.
AT: That is the result(the reduction I spoke of), however we are talking concept more than implemetnation, considering that implementation is obviously goign to take the shortcuts needed. Infact your point probably does a great deal more to point out the flaw in Cyrus's theory than it does mine. Null is null is null, no argument yet has made sense when you consider that to be true. Stop consdiering Nullable<T> as a type and consider it as a container that can be null and I think you will all have less trouble with this...Anonymous
June 11, 2004
The comment has been removedAnonymous
June 11, 2004
The comment has been removedAnonymous
June 11, 2004
Nice comment AT. After I've read it - was able to find solution.
Ups.. Sorry. Talking to myself ;o)
To solve problems with NullAble<NullAble<T>> you must follow OO priciples and separate all cases you realy need from generic NullAble one.
In Cyrus example ages.Find must return not simply int?? but SearchResult<NullAble<int>>.
SearchResult is similar to (or even based on) NullAble.
struct SearchResult<T> {
public bool WasFound() { get; }
public bool HasValue() { get; }
T Value { get; } // Can result in two different exceptions - NotFoundException of ArgumentNullException if something wrong
NullAble<T> NullAbleValue { get; } // Can result only in NotFoundException
//Not found
public SearchResult();
// Found value
public SearchResult(T value);
// Something was found or null
public SearchResult(NullAble<T> value);
}
As well in my case it must be PersonSearchResult<DepartmentSearchResult<NullAble<string>>>
This will solve problem if somebody is trying to access value without validity check. In my example it will result in correct exception (PersonNotFound or DeparmentNotFound) to this situation, not simply a NullReference (ArgumentNullException).Anonymous
June 11, 2004
Mark: No, null is null. There is a difference between something being found and something being null. As AT expresses in his last post, using Nullable to express that an object is found is a volation, however Nullable does make a nice underlying basis for a NullObject or insurance for a ResultObject pattern. While your pattern shows a way to make one null state(note not null) not equal to another null state, it simply won't work with Nullable. That pattern not only breaks the concept of null, it breaks OO principals. In esscense, Nullable<int> x = null is already null, there is no extra way to express its nullness. You are simply trying to tack on concepts that don't fit in with the concept of null, which you shouldn't do. Nullable only defines two states(null and not null) and Nullable is null when a Nullable with state null is its value as Nullable expresses whether its value is null or not.
AT: I like that pattern quite a bit. It allows for a flexible multiple state solution, which Nullable doesn't and answers every challange so far.Anonymous
June 11, 2004
Daniel: At an absolute minimum, there are two meanings of "null" - reference null and value null. When it comes to the value nulls, Cyrus put it quite well when he said that "nullable types allow you to encode that extra bit of boolean state". Obviously what this boolean state indicates is going to vary quite a lot in real world uses of nullable types. Most commonly it will be a database NULL or a "not found" indicator, but there could be others. So if we were searching a database table for a given ID and returning the value of a nullable column, we might use
int?? x = GetNullableColumnValue(String searchKey);
in which case x.HasValue would indicate whether or not the relevant ID was found, and x.Value would be the value of the column (which might be NULL, of course). For the sake of argument, I am assuming that a nullable return to indicate found or not is preferable to an exception for performance reasons.
Please note that I'm not claiming that there aren't generally better alternatives to using underlying nullable types, just that they can be used in a sensible way and we shouldn't be prohibiting them or restricting them just for the sake of it.Anonymous
June 12, 2004
Orion: "Nullable<Nullable<T>> to Nullable<T>.
" The language can't. Nullable is simply a generic struct. Also, While it might not allow you to write:
int??
How would you deal with the following:
T? SomeMethod<T>();
SomeMethod<int?>();
We might be able to catch that. But what about generic types that return instantiations of other generic types that will eventually boil down in a T?? being returned.Anonymous
June 12, 2004
It sounds like (to me) that people believe that Nullable should be a very special type. If so, then I agree. I'm absolutely fine with reference types not being nullable and value types being nullable, and having the actual "Nullable" type which is neither value nor reference.
This would prevent Nullable<Nullable<A>>.
It seems like you could get the right semantics that way. It seems to me that Nullable needs to be something that the runtime provides, not some weird struct with rules that behave inconsistantly.Anonymous
June 12, 2004
Daniel: " I do think that the semantics of Nullable(implicit operators and hoisting) should result in C# semantics that effectivly cause reduction"
But nothing else is hoisted (like methods). So then you don't have reduction. I think you are focussing too much on things like integers, and not on regular structs that are dealt with all the time.Anonymous
June 12, 2004
Daniel: "however, that Nullable<T> supports implicit conversion and hoisted operators, therefore can be considered to be T in all ways(except null and type comparison),"
T? is not implicitly convertible to T. It's explicitly convertible. At least I think it is :-)Anonymous
June 12, 2004
AT: "In Cyrus example ages.Find must return not simply int?? but SearchResult<NullAble<int>>."
Why?
If my collection is a collection of T, i.e. an ICollection<T>. Then Find returning a T? is good enough. Find(...).HasValue, then the collection contained the value and Find(...).Value is the value. If Find(...).HasValue is false, then it just didn't contain any values meating that criteria.
However, that works for every struct except Nullable<>. If I have an ICollection<Nullable<T>> then Find's return value is ambiguous. Why add a concept into the language that introduces ambiguity when you didn't need to?Anonymous
June 12, 2004
Cyrus: You are right on the methods, I hadn't considered methods as an issue. However, you have still yet to address the simple concept of Nullable being nullable. It just seems utterly insane, to me, to think that assinging a null Nullable to a Nullable means anything other than the Nullable is null. When you set a value to null, it is null.
Nullable is a special type, in the language, in that it is the only struct that can be null. int can't be, decimal can't be, Guid can't be, Point can't be, Nullable can. Since one valid state for Nullable is null, then there is no sense in expressing null for it.
I really disagree with your conclusions and reasoning here, but I don't think I'm going to change your mind. Perhaps a language designer or compiler person may be able to express this more exactly. I'm pretty sure my reasoning is right(and the right thing to do), I just lack the ability to explain it.
It comes down to a nullablilty meaning a value can be null. It makes sense when you look at it right. I really think most of your examples are either incorrect or illogical. For example, your Find example has a huge flaw: you can't use nullable types in most generic situations. T? Find on ICollection<T> is an incorrectly written interface. You can't rely on nullables to express found or not found, only null or not null. If null means found or not found, that is perhaps ok. This same issue that exists in the reference world. You wouldn't EVER dream of returning null to mean not found in some cases and found but null value in others, would you(look at the mess HashTable made of this)? Why are you trying to treat nullable value types differently than you would reference types? Why are you trying to use the SAME concept to express two DIFFERENT concepts in the same place? Why don't you think that Nullable::HasValue=true should mean that my value isn't null. Under your system HasValue=true means "the underlying value could be null, maybe, what I say really doesn't matter because I only have an underlying value that may or may not think its null." I expect HasValue to mean the value IS or ISN'T null, not a value that may express nullality of its own. Nullable can be null, therefore a Nullable around a null Nullable is null, period.
I am done with this argument, however. Its taking up alot of my time saying the same thing over and over and over again.Anonymous
June 12, 2004
Cyrus:
Collection<T> find() can return SearchResult<T> : NullAble<T> instead of SearchResult<NullAble<T>>.
HasValue will return false if you are unable to access value but you can check exact reasoning for this using bool ValueIsNull, bool ValueNotFound flags or any other way.
I may repeat - but this will result in valid Exception to user if trying to access value without validation. In addition to value and HasValue flag we use type id of result.
This way you will not need NullAble<NullAble<T>>.
But this is somethat hard or almost impossible while writing generic Collection<T> but use of it as Collection<NullAble<T>>Anonymous
June 13, 2004
Daniel: I'm not disagreeing with anything you are saying about things being null :-)
The purpose behind what I was saying was that I don't think what you or I want can be accomplished by using this generic struct called Nullable<T>.
You want Nullable to be a special type. I want nullable to be a special type. But it isn't. This is why these contradictions arise that make things so confusing.
Also: I want Nullable<T> to express what it's interface seems to express. Namely the presence or absence of a value. That's why the properties on it are: Has Value and Value. The concept of having/not having a value seem to be exactly what a collection would return when you asked it to find something :-)
But I agree that if Nullable doesn't work that way then collections shouldn't use it. I'll repeat that. I agree :-)
I think we both want a Nullable struct to be the same as a regular reference type (wrt null). However, I just don't see that being possible with the current issues. That's all.Anonymous
June 13, 2004
AT: Ah. I get it now.
SearchResult<T> extends Nullable<T>. I didn't get that before.
Well, I sort of get it. And I sort of don't ;-)
Let me reread everything you said to see if it's clearer now!Anonymous
June 13, 2004
Orion: "In C# because nullable is handled with a class int?? and int? are different. Mathematically they shouldn't be and int?? is nonsensical. int?? should always reduce down to int? wherever it pops up. Unfortunately that's probably pretty hard to do with C# the way it is now. "
Agreed. How would you go about doing it?Anonymous
June 13, 2004
Daniel: " You can't treat Nullable as null when HasValue is false in some situations and not in others. That would be a mistake of feature killing caliber. "
I have never, ever, tried to do that. (At least, I don't think I have).
When 'HasValue' is false, getting 'Value' always throws an exception. When 'HasValue' is true, getting 'Value' returns the object that was wrapped into the nullable generic.Anonymous
June 13, 2004
Orion: "The language really should prevent the idea of using Nullable as a template parameter for itself. That or have special semantics that reduce Nullable<Nullable<T>> to Nullable<T>. "
We could do that in the following manner:
public sealed class Nullable<T> where T : struct
{
}
What would you think about that? Then you could never run into the scenario where you had Nullable<Nullable<Something>>. That's because Nullable<Something> would be a class, not a struct, and you couldn't instantiate the outer Nullable type with a non-struct type.
This would have clear semantic meaning for me. You would be elevating the value-type up into a reference type. It sort of seems like it would be similar to the boxing that occurs in java from int to java.lang.Integer.Anonymous
June 13, 2004
Daniel: "You wouldn't EVER dream of returning null to mean not found in some cases and found but null value in others, would you(look at the mess HashTable made of this)?"
I see. That's a very good point. It looks like I'm mixing up the difference between having a value and having a null value. (Sorry to all of those who've been trying to get me to see that from the beginning).
Here's how I saw it (before) when I was thinking about it in a 'no-value' sense:
class HashTable<K,V> {
V? Get(K k);
}
So if you tried to get a value from teh table based on the key, you'd either get it (the HasValue/Value case), or you wouldn't (the !HasValue case).
However, in the Null/Non-null case it doesn't make much sense. Especially when you think about say a HashTable<int,string>. Because I think you'd expect that 'null' and a Nullable with no value to be the same thing. And in my system that would break down.
i.e. I could create a Nullable<string> that said "yes i have a value" but which would then return 'null' on the Value property.
So. I think my major issue is in two parts.
a) I find the properties are very badly named. But that's probably just me. However, I see having a value and being null as being two different things. i.e. I see:
string s;
string t = null;
s has no value. t has a value, but it's null. This is probably where my confusion comes in.
b) Nullable is supposed to be special, but that breaks down because in reality it's just a generic struct. So the rules are now inconsistant with regard to it. I hate inconsistancy :-)Anonymous
June 13, 2004
There does seem to be a lot of confusion as to what null is exactly. Null is the empty set ({}). That's it. That's all it means. What you do with the empty set is up to you.
Remember types are just a form of constraint of the set that the variable represents. All variables have a certain cardinality (number of elements in a set). Sometimes the set is empty, often it isn't.
Reference, non-array types in C# can have a size of 0 (the null set) or 1.
Value types always have a size of 1. (without INullable).
Since variable are always sets they should be easy to compare.
string s;
string t = null;
Since s is not assigned to above, C# leaves its value the empty set ({}) also called null. t above is deliberately set to null (aka the empty set ({})). If you compare the sets against each other, they will be the same.
With the hashtable example there is a connundrum only because of the multiple uses of the empty set. For awhile now, libraries in various languages have made use of the empty set (aka null) for both a possible stored value and to indicate that the key is not in the dictionary.
Two solutions have presented themselves. One is throwing an exception when a value isn't found. The second is a find method that returns a boolean indicating whether or not it is found and a out parameter that can return the value, being populated only if the item is found.
The problem with dictionaries and the option of not finding an object is that return values are always populated. However a key might not always be found. Therefore without exceptions return values can't be used to return values associated with keys without introducing ambiguity.
This isn't just true for dictionaries but for all returned values (via return or out parameter) that are populated only when a condition is met. Any system that tries to use the empty set (aka null) to combine this information into a single return is going to artificially limit itself and result in an impure solution (and therefore introduce problems).
Orion AdrianAnonymous
June 13, 2004
The comment has been removedAnonymous
June 13, 2004
The comment has been removedAnonymous
June 13, 2004
Orion,
You suggest that C# and .NET fundamentals themselves should allow any struct to have the null value. I don't think this is a simpler/better solution because there are no good ways to do that. I can think of two ways to do that:
1) All the structs should have a pointer on stack (which can be null or not), and if the pointer is not null, their values to be stored in heap. But hey, this is the same as class, so this is not a good idea. The idea of structs WAS by design having them have their values stored on the stack.
2) All the structs should implicitely have a HasValue (or unnamed) hidden boolean value stored with their value, and managed by the core of .NET. However, in this situation, any struct T is in fact stored as an Nullable<T>. I agree that this way you hide the special Nullable type and its possible specifications inconsistences, but at the cost of storing one bit (and in fact one byte) for all the struct types anyone can imagine. So if you previously have 1000 integers within a program, and they previously took 4000 bytes from your memory at runtime, they will now take 5000 bytes. Do we really have that much memory to allow this?
And Cyrus,
I would not vote for your proposed solution either (changing Nullable to a sealed class instead of letting it a struct) because this way you can have a Nullable<int> be null (pointer null on stack) or Nullable<int> be null (pointer not null but HasValue is false). And there are too many boxing/unboxing operations involved (or am I wrong here?). However, do we really have that fast processors?
I would vote for managing the inconsistency at the Specifications documentation level. Explicitely say that Nullable is a special type but leave it a struct.Anonymous
June 13, 2004
One correction. I have misunderstood the Cyrus' proposal about changing the Nullable struct into a class, first. I saw now that Cyrus also removes the HasValue boolean property from the implementation, as the Nullable<T> class is now derived from the T struct and does not contain any supplemental properties.
This seems a lot better (sorry Cyrus, previously somehow, I though that you kept the same implementation, and only changed struct to sealed class - that's what happens when you read diagonally :-)) but however, the boxing and unboxing for any nullable type will occur and I'm not sure if this is really OK with anyone...
However, I don't like inconsistencies AT ALL, either, especially in the Specifications!Anonymous
June 13, 2004
Orion Adrian: Sets in addition to variables can contain others sets.
Example { {'a',b','c'}, {'1','2'}, {}}.
So why there can not be {{1}} or {{}} ? Check out my link to Von Neumann theory.Anonymous
June 14, 2004
Sorin: First, about memory: It seems a pity to fudge up your language because you're worried about taking up 1 extra k of memory.
Second: How do you know it will take up 1 more byte per instance? The implementation is up to the runtime and they could do whatever they wanted (including saving nullable state somewhere totally different <a la the way databases do it>).Anonymous
June 14, 2004
Sorin: I'm going to do a perf analysis today to see the cost of moving to a sealed class version of nullable. I'll let you know the result later.Anonymous
June 14, 2004
The comment has been removedAnonymous
June 14, 2004
Cyrus,
You are right about the fact that the boolean bits will not eat up as much as 1 kb per 1024 booleans, but they will still eat up some space: at least 128 bytes / 1024 booleans (unless you don't use some zip algorithm to compress even more) :-)
When I assumed that the runtime will probably use 1 byte / boolean, I thought that the runtime itself will use bool (System.Boolean) type for storing those boolean values, for the sake of the beauty of the implementation code. (Yeah, I know, it's not the case here, where optimal is needed).
Anyway, I will cry for having 128 bytes eaten up by the booleans for my 1024 integers variables, but I will eventually probably get happier since all the values could then be null. If you pay the right price, you get the right prize :-)
About the class/struct change, I am definately waiting for your results, and I really hope that they will be successful, meaning that the class approach can be used with very little overhead...Anonymous
June 15, 2004
A couple of points on implementation.
My point was to make all variables nullable, but that does not restrict the system from allowing the programmer from specifying that a particular variable cannot be null. The beauty of that is that I still get the prize, but I don't loose performance when I, as the programmer, am smart.
If you think about it, you have very few areas where nullable value-type variables are going to pop up, but those few instances are all important.
My suggestion is that nullability for value-type variables be brought into the language as a whole and not just a struct add-on.
If we could have done this from the beginning you would
1) remove struct (class is enough; more later!)
2) make all types consistent in their usage of nullability and min and max cardinality
3) use a profiler to do optimizations and not the programmer.
On the profiler thing... it would be really nice if the programmer just had to worry about the algorithm and not the performance. While lots of work for the writer of the compiler having a profiler decide when to use the stack and when to use the heap seems a lot more useful than making the programmer make those decisions.
Remember the compiler has to be written once, but tens or hundreds of millions of lines of code will be written with it. The language and the compiler should make the life of the programmer as easy as possible.
So for a method that has 32 nullable variables or fewer, a single int could be used to hold the null bits for all that data. Also remember nullability and stack/heap are artificially linked. There's no reason that you couldn't have nullable stack-based variables.
Orion AdrianAnonymous
June 15, 2004
Sorin: I have some interesting results. Finding out if I can post them.
Orion: Excellent points. Very similar to how many databases handle null'able columns. I like that.Anonymous
June 16, 2004
Cyrus,
If the results are good enough, I think that making Nullable a class instead of a struct would be nice, and would remove the specifications inconsistencies.
Orion,
That profiler thing is a very good idea. And also is the ability to let the user specify (maybe through a modifier) when a variable must not be nullable instead of otherwise (Nullable struct). However, if the user doesn't specify anything, the compiler may decide that that variable shouldn't be null.
However, I want to ask why do you say that "struct" can be removed from the language. Now, structs have the property that their values are stored on the stack and not in heap like classes' values. If you remove struct and allow the compiler decide which variables should be nullable and not (based on compiler intelligence), that's OK, but how the user/compiler decides when a type is a value type (struct) and when is it a reference type?
I know, you said something about detailing that later, but I'm very curious about the ideas you have about it!Anonymous
June 16, 2004
Cyrus and Orion,
I must admit that as I think about it, I like more and more Orion's idea about adding a nullability concept within the core of .NET and the C# language, for ALL the types and drop that Nullable<T> idea.
However, I want to ask for your opinion/comments about this issue: do you think that programmers communities will or won't be shocked about such a change? How do you think programmers would react if they would find out that the new version of .NET core/C# language allows null for any struct, and that they should specify when a type should not allow to have null values, or that the compiler will try to decide for them... Would they be scared that some old code will not be running in the new version? And what about the "programmers minds backwards compatibility" (the way they fill about knowin a language and now it changes in its core)?
Personally, I usually LOVE when a new core comes up with new awesome things, even if the backwards compatibility is not always maintained. I happily rewrote my Visual J++ 6 applications in C# before the Java-to-C# converter was live, for example. I don't have a problem with that. I always LOVE innovation, even if I have to work again to make my old work running again... But not anybody likes that. I'm this way because I'm passional about technology and I always like the more consistent, and intelligent solution for the core of the technologies. I also am/was a beta tester for many products and LIKED to have crashes because I knew this way I can help removing them in the future... I even install betas on my primary machine! Pretty silly, no? But I like it. And I'm free to do it :-)
So the question is about what do you think about the others. I cannot tell because I'm different, and cannot see it clearly :-)
Thanks!Anonymous
August 20, 2005
얼마전에 Microsoft의 Developer Division 대빵인 Somasegar가 Nulls not missing anymore라는 글로 Nullable Types의 DCR(Design...Anonymous
August 21, 2005
얼마전에 Microsoft의 Developer Division 대빵인 Somasegar가 Nulls not missing anymore라는 글로 Nullable Types의 DCR(Design...Anonymous
June 08, 2009
PingBack from http://cellulitecreamsite.info/story.php?id=433Anonymous
June 16, 2009
PingBack from http://fixmycrediteasily.info/story.php?id=779